增加客户端独立部署功能

ossnew
isummer 2 years ago
parent 8a8d2f2400
commit d1a5d69ac8
  1. 26
      fir_client/src/components/apps/FirAppInfossecurity.vue
  2. 4
      fir_client/src/components/apps/FirAppInfostimeline.vue
  3. 2
      fir_client/src/components/apps/FirApps.vue
  4. 6
      fir_client/src/components/user/FirSuperSignBase.vue
  5. 100
      fir_client/src/components/user/FirUserDomain.vue
  6. 4
      fir_client/src/restful/index.js
  7. 30
      fir_ser/api/views/personalconfig.py
  8. 3
      fir_ser/api/views/uploads.py
  9. 8
      fir_ser/common/core/sysconfig.py
  10. 3
      fir_ser/config.py

@ -109,12 +109,26 @@
prop="token"
>
<template slot-scope="scope">
<el-tooltip content="点击复制">
<el-link v-clipboard:copy="format_copy_text(scope.row.token)" v-clipboard:success="copy_success"
:underline="false">
{{ scope.row.token }}
</el-link>
</el-tooltip>
<el-popover
placement="top-start"
trigger="hover"
width="200">
<p>
<el-tag v-clipboard:copy="scope.row.token" v-clipboard:success="copy_success" type="success">
<el-link :underline="false">点击复制授权码</el-link>
</el-tag>
</p>
<p>
<el-tag v-clipboard:copy="format_copy_text(scope.row.token)" v-clipboard:success="copy_success">
<el-link :underline="false">点击复制授权码下载链接</el-link>
</el-tag>
</p>
<el-link slot="reference" :underline="false">{{ scope.row.token }}</el-link>
</el-popover>
</template>
</el-table-column>

@ -4,7 +4,7 @@
:color="app.master_color"
:timestamp="app.created_time|formatdatatimeline"
placement="top">
<el-card>
<el-card :body-style="{border: app.is_master ? '#2abb9d 1px solid' : ''}">
<div class="directive-view-release">
<i v-if="! app.is_master" class="el-icon-cloudy"/>
@ -80,7 +80,7 @@
<el-button v-if="!currentapp.issupersign" class="tooltip-top" @click="previewRelease(app)"><i
class="el-icon-view"/> <span
class="ng-binding">预览</span>
class="ng-binding">预览该版本</span>
</el-button>
<el-button v-if="! app.is_master" class="tooltip-top" @click="make_master_release(app)">

@ -222,7 +222,7 @@
<el-row :gutter="20" style="margin-top: 10px;">
<el-col :span="18">
<el-input v-model="short">
<template slot="prepend">{{ analyseappinfo.short_domain_name }}/</template>
<template slot="prepend">{{ analyseappinfo.short_domain_name }}</template>
</el-input>
</el-col>
</el-row>

@ -1962,7 +1962,7 @@ export default {
}
}, {
methods: 'PUT', data: {config_key: info.key, config_value: info.value}
})
}, 'super_sign')
},
updateSignConfig() {
personalConfigInfo(data => {
@ -1974,7 +1974,7 @@ export default {
}
}, {
methods: 'DELETE'
})
}, 'super_sign')
},
signConfigFun() {
personalConfigInfo(data => {
@ -1986,7 +1986,7 @@ export default {
}
}, {
methods: 'GET'
})
}, 'super_sign')
},
saveblackdevice() {
blackDeviceUtil(data => {

@ -11,6 +11,47 @@
:domain_state="true" :domain_type="current_domain_info.domain_type"
transitionName="bind-app-domain"/>
</el-dialog>
<el-dialog
:close-on-click-modal="false"
:close-on-press-escape="false"
:visible.sync="configVisible"
center
title="下载页部署配置"
width="666px">
<el-card v-for="info in config_lists" :key="info.key" class="box-card" shadow="hover"
style="margin-bottom: 10px">
<div slot="header" class="clearfix">
<span><el-tag size="medium" type="info">配置KEY</el-tag> <el-tag size="medium">{{ info.key }}</el-tag></span>
<div style="float: right">
<el-switch
v-model="info.value"
active-color="#13ce66"
active-text="启用"
active-value="true"
inactive-color="#ff4949"
inactive-text="关闭"
inactive-value="false"
@change="changeConfig(info)">
</el-switch>
</div>
</div>
<el-tag size="medium" type="info">描述信息</el-tag>
{{ info.title }}
<div v-if="short_download_uri" style="margin-top: 20px">
<el-tag size="medium" type="success">下载页部署源码及操作文档</el-tag>&nbsp;&nbsp;&nbsp;&nbsp;
<el-link :href="short_download_uri" target="_blank">点击下载</el-link>
</div>
</el-card>
<div slot="footer" class="dialog-footer">
<el-button @click="updateConfig">恢复默认值</el-button>
<el-button @click="configVisible=false">取消</el-button>
</div>
</el-dialog>
<div>
<el-input
v-model="search_key"
@ -21,6 +62,11 @@
搜索
</el-button>
<div style="float: right">
<el-tooltip content="下载页配置,可以定制部署私有下载页">
<el-button plain type="primary" @click="configFun">
下载页配置
</el-button>
</el-tooltip>
<el-tooltip content="应用安装下载页,多个下载页域名可以避免域名被封导致其他应用也无法访问">
<el-button plain type="primary" @click="$store.dispatch('dodomainaction', 1)">
添加下载页域名
@ -154,7 +200,7 @@
<script>
import {domaininfo} from "@/restful";
import {domaininfo, personalConfigInfo} from "@/restful";
import BindDomain from "@/components/base/BindDomain";
import {format_choices, getUserInfoFun} from '@/utils'
import {format_time} from "@/utils/base/utils";
@ -172,9 +218,61 @@ export default {
domain_type_choices: [],
current_domain_info: {'domain_type': 1, 'app_id': null},
loading: false,
configVisible: false,
config_lists: [],
short_download_uri: ''
}
},
methods: {
configFun() {
personalConfigInfo(data => {
if (data.code === 1000) {
if (data.data.length === 1) {
this.short_download_uri = data.data[0].value
}
} else {
this.$message.error("获取数据失败了 " + data.msg)
}
}, {
methods: 'GET'
}, 'short_download_uri')
personalConfigInfo(data => {
if (data.code === 1000) {
this.config_lists = data.data
this.configVisible = true
} else {
this.$message.error("获取数据失败了 " + data.msg)
}
}, {
methods: 'GET'
}, 'preview_route')
},
changeConfig(info) {
personalConfigInfo(data => {
if (data.code === 1000) {
this.$message.success("操作成功")
this.configFun()
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, {
methods: 'PUT', data: {config_key: info.key, config_value: info.value}
}, 'preview_route')
},
updateConfig() {
personalConfigInfo(data => {
if (data.code === 1000) {
this.$message.success("操作成功")
this.configFun()
} else {
this.$message.error("操作失败了 " + data.msg)
}
}, {
methods: 'DELETE'
}, 'preview_route')
},
saveWeight(domain_info) {
domaininfo(data => {
if (data.code === 1000) {

@ -692,10 +692,10 @@ export function notifyConfigInfo(callBack, params) {
}
/**消息配置 */
export function personalConfigInfo(callBack, params) {
export function personalConfigInfo(callBack, params, super_sign) {
getData(
params.methods,
USERSEVER + '/personal/config',
USERSEVER + '/personal/config?config_type=' + super_sign,
params.data,
data => {
callBack(data);

@ -15,10 +15,24 @@ from api.utils.response import BaseResponse
from api.utils.serializer import PersonalConfigSerializer
from common.core.auth import ExpiringTokenAuthentication, SuperSignPermission
from common.core.sysconfig import UserConfig, Config
from common.utils.caches import del_cache_storage
logger = logging.getLogger(__name__)
def get_personal_config(request):
config_type = request.query_params.get("config_type", None)
if config_type == 'super_sign':
personal_config = Config.DEVELOPER_STATUS_CONFIG
elif config_type == 'preview_route':
personal_config = ['PREVIEW_ROUTE_HASH']
elif config_type == 'short_download_uri':
personal_config = ['DOWNLOAD_DEPLOYMENT_URL']
else:
personal_config = []
return personal_config
class PersonalConfigView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
permission_classes = [SuperSignPermission, ]
@ -27,11 +41,11 @@ class PersonalConfigView(APIView):
res = BaseResponse()
config_key = request.query_params.get("config_key", None)
developer_personal_config = Config.DEVELOPER_STATUS_CONFIG
personal_config = get_personal_config(request)
personal_config_queryset = UserPersonalConfig.objects.filter(user_id=request.user,
key__in=developer_personal_config)
if personal_config_queryset.count() != len(developer_personal_config):
for key in developer_personal_config:
key__in=personal_config)
if personal_config_queryset.count() != len(personal_config):
for key in personal_config:
UserConfig(request.user).set_default_value(key)
if config_key:
personal_config_queryset = personal_config_queryset.filter(key=config_key)
@ -46,15 +60,17 @@ 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 Config.DEVELOPER_STATUS_CONFIG:
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)
return Response(res.dict)
def delete(self, request):
res = BaseResponse()
developer_personal_config = Config.DEVELOPER_STATUS_CONFIG
for key in developer_personal_config:
for key in get_personal_config(request):
UserConfig(request.user).del_value(key)
Config.invalid_config_cache(f'{key}_DES')
if key == 'preview_route':
# 清理缓存
del_cache_storage(request.user)
return Response(res.dict)

@ -19,7 +19,7 @@ from api.utils.signalutils import run_signal_resign_utils
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.sysconfig import Config
from common.core.sysconfig import Config, UserConfig
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, make_token
@ -92,6 +92,7 @@ class AppAnalyseView(APIView):
else:
upload_key = release_id + '.apk' + settings.FILE_UPLOAD_TMP_KEY
png_key = png_id + '.png' + settings.FILE_UPLOAD_TMP_KEY
short_domain_name = f"{short_domain_name}/{'#/' if UserConfig(request.user).PREVIEW_ROUTE_HASH else ''}"
storage = Storage(request.user)
storage_type = storage.get_storage_type()
upload_token = storage.get_upload_token(upload_key)

@ -157,6 +157,10 @@ class DomainConfCache(ConfigCacheBase):
def WEB_DOMAIN(self):
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)
class BaseConfCache(ConfigCacheBase):
def __init__(self, *args, **kwargs):
@ -449,6 +453,10 @@ class UserPersonalConfKeyCache(ConfigCacheBase):
def PREVIEW_ROUTE_HASH(self):
return super().get_value('PREVIEW_ROUTE_HASH', USERPERSONALCONFIGKEY.PREVIEW_ROUTE_HASH)
@property
def PREVIEW_ROUTE_HASH_DES(self):
return super().get_value('PREVIEW_ROUTE_HASH_DES', USERPERSONALCONFIGKEY.PREVIEW_ROUTE_HASH_DES)
class ConfigDescriptionCache(ConfigCacheBase):
def __init__(self, *args, **kwargs):

@ -14,6 +14,7 @@ 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"
class BASECONF(object):
@ -470,6 +471,8 @@ class USERPERSONALCONFIGKEY(object):
'DEVELOPER_ABNORMAL_DEVICE_WRITE']
PREVIEW_ROUTE_HASH = False # 预览路由模式是否是hash, 如果使用hash模式,url中就会存在“#“符号,这个符号后面的是路径。
PREVIEW_ROUTE_HASH_DES = '【下载页路由hash模式】:使用hash模式,url中就会存在“#“符号,这个符号后面的是路径。非hash模式,需要nginx进行额外配置'
class OSSSTORAGECONF(object):
STORAGE_FREE_CAPACITY = 2048 * 1024 * 1024 # 单位byte 2G

Loading…
Cancel
Save