增加下载权限控制

super_signature
nineven 5 years ago
parent 68c8db9339
commit 47f2f534c7
  1. 84
      fir_client/src/components/FirAppInfosBase.vue
  2. 8
      fir_client/src/components/FirAppInfosbaseinfo.vue
  3. 40
      fir_client/src/components/FirAppInfoscombo.vue
  4. 89
      fir_client/src/components/FirAppInfosdevices.vue
  5. 246
      fir_client/src/components/FirAppInfossecurity.vue
  6. 25
      fir_client/src/components/FirDownload.vue
  7. 12
      fir_client/src/router/index.js
  8. 66
      fir_download/src/components/FirDownload.vue
  9. 28
      fir_ser/api/migrations/0021_auto_20200410_1141.py
  10. 18
      fir_ser/api/migrations/0022_apps_isshow.py
  11. 18
      fir_ser/api/migrations/0023_appreleaseinfo_udid.py
  12. 4
      fir_ser/api/models.py
  13. 3
      fir_ser/api/utils/app/apputils.py
  14. 77
      fir_ser/api/utils/serializer.py
  15. 26
      fir_ser/api/utils/storage/caches.py
  16. 2
      fir_ser/api/views/apps.py
  17. 18
      fir_ser/api/views/download.py
  18. 3
      fir_ser/api/views/uploads.py

@ -2,7 +2,7 @@
<el-main>
<div class="page-app app-info">
<div class="banner">
<div class="middle-wrapper" ref="mwp">
<div class="middle-wrapper" >
<div @click="defaulttimeline">
<img :src="icon_url" class="appicon" style="width:100px; height:100px">
</div>
@ -13,17 +13,10 @@
<span class="bundleid ng-binding">BundleID<b class="ng-binding">&nbsp;&nbsp;{{ appinfos.bundle_id }}</b></span>
<span class="version ng-scope">{{ master_release.minimum_os_version }}&nbsp; 或者高版本</span>
</div>
<div class="actions">
<!-- <el-button class="upload" icon="el-icon-cloudy">-->
<!-- 上传新版本-->
<!-- </el-button>-->
<el-button @click="appDownload" class="download" icon="el-icon-view">
预览
</el-button>
</div>
<div class="tabs-container">
@ -33,10 +26,17 @@
</el-col>
<el-col :span="3">
<a class="" ref="security" @click="security"><i class="el-icon-set-up"></i>权限管理</a>
</el-col>
<el-col :span="3">
<a class="" ref="combo" @click="combo"><i class="el-icon-copy-document"
style="transform:rotateX(180deg);"></i>应用合并</a>
</el-col>
<el-col :span="3" v-if="appinfos.type">
<a class="" ref="devices" @click="devices"><i class="el-icon-mobile-phone"></i>设备列表</a>
</el-col>
</el-row>
</div>
</div>
@ -48,7 +48,6 @@
v-model="$store.state.appInfoIndex[0]"
range
:show-tooltip="false"
:change="slidevents()"
:max="100">
</el-slider>
</div>
@ -57,10 +56,7 @@
<el-container style="padding-top: 20px;max-width: 96%">
<router-view></router-view>
</el-container>
</div>
</div>
</el-main>
@ -80,44 +76,43 @@
activity: {
editing: false
},
slidvalue: [[5, 5], [5, 5]]
}
},
methods: {
setfunactive(item,index){
for (let key in this.$refs) {
if(key === item){
this.$refs[key].classList.add('active');
this.$store.dispatch('doappInfoIndex', [[index, index], [index, index]]);
}else {
this.$refs[key].classList.remove('active');
}
}
},
appDownload() {
this.$router.push({name: 'FirDownload', params: { short: this.appinfos.short }})
},
combo() {
this.$router.push({name: 'FirAppInfoscombo'});
this.$store.dispatch('doappInfoIndex', [[31, 31], [31, 31]]);
this.$refs.combo.classList.add('active');
this.$refs.baseinfo.classList.remove('active');
defaulttimeline() {
this.setfunactive('timeline',5);
this.$router.push({name: 'FirAppInfostimeline'});
},
baseinfo() {
this.$refs.baseinfo.classList.add('active');
this.$refs.combo.classList.remove('active');
this.setfunactive('baseinfo',18);
this.$router.push({name: 'FirAppInfosbaseinfo'});
this.$store.dispatch('doappInfoIndex', [[18, 18], [18, 18]]);
},
security(){
this.setfunactive('security',31);
this.$router.push({name: 'FirAppInfossecurity'});
},
slidevents() {
// let index = this.$store.state.appInfoIndex[1];
// this.$store.dispatch('doappInfoIndex',[index,index]);
// eslint-disable-next-line no-console
// console.log(index);
combo() {
this.setfunactive('combo',44);
this.$router.push({name: 'FirAppInfoscombo'});
},
defaulttimeline() {
this.$refs.baseinfo.classList.remove('active');
this.$refs.combo.classList.remove('active');
this.$router.push({name: 'FirAppInfostimeline'});
this.$store.dispatch('doappInfoIndex', [[5, 5], [5, 5]]);
devices(){
this.setfunactive('devices',57);
this.$router.push({name: 'FirAppInfosdevices'});
},
}, created() {
}, filters: {
@ -132,21 +127,8 @@
},
},
computed: {
getBH: function () {
let sch = this.$refs.appinfomain.scrollHeight;
if (sch === 0 || sch < window.innerHeight) {
sch = window.innerHeight;
} else {
sch += 130;
}
return sch;
}
}, mounted() {
getappinfos(data => {
if (data.code === 1000) {
@ -161,14 +143,12 @@
// eslint-disable-next-line no-console
console.log("失败了");
}
}, {
"app_id": this.$route.params.id
});
if(this.$store.state.currentapp.master_release){
this.icon_url = this.$store.state.currentapp.master_release.icon_url
}
// this.$store.dispatch('dosetAh',this.getBH);
},watch:{
'$store.state.currentapp.master_release.icon_url':function () {
this.icon_url = this.$store.state.currentapp.master_release.icon_url

@ -186,22 +186,18 @@
const isLt2M = file.size / 1024 / 1024 < 2;
if(file.type === 'image/jpeg' || file.type === 'image/png'|| file.type === 'image/jpg'){
if (isLt2M) {
// return true;
uploadimgs(data => {
if (data.code === 1000) {
// eslint-disable-next-line no-console
console.log(data.data);
let certinfo=data.data;
this.uploadtostorage(file,certinfo);
}
},{'methods':false,'data':{'app_id':this.currentapp.app_id,'upload_key':file.name,'ftype':'app'}});
}
else{
this.$message.error('上传头像图片大小不能超过 2MB!');
this.$message.error('上传应用图片大小不能超过 2MB!');
}
}else {
this.$message.error('上传头像图片只能是 JPG/PNG/JPEG 格式!');
this.$message.error('上传应用图片只能是 JPG/PNG/JPEG 格式!');
}
return false;

@ -1,7 +1,7 @@
<template>
<div class="apps-app-combo page-tabcontent ">
<div class="middle-wrapper">
<div v-if="has_combo " class="request-wrapper">
<div class="request-wrapper" v-if="has_combo ">
<p class="lead text-center ng-scope">已经与 <b>{{ has_combo.name }}</b> 合并</p>
<table>
<tr>
@ -11,26 +11,26 @@
</tr>
<tr>
<td>
<div class="icon"><img class="ng-isolate-scope" :src="cmaster_release.icon_url">
<div class="icon"><img :src="cmaster_release.icon_url" class="ng-isolate-scope">
</div>
</td>
<td><i class="icon-combo"></i></td>
<td>
<div class="icon"><img class="ng-isolate-scope" :src="hmaster_release.icon_url">
<div class="icon"><img :src="hmaster_release.icon_url" class="ng-isolate-scope">
</div>
</td>
</tr>
<tr>
<td colspan="3" class="actions">
<td class="actions" colspan="3">
<!-- <a class="btn btn-link " @click="each_confirm"><b>解除合并</b></a>-->
<el-button round @click="each_confirm">解除合并</el-button>
<el-button @click="each_confirm" round>解除合并</el-button>
</td>
</tr>
</table>
</div>
<div v-else>
<div class="icon-container text-center">
<img width="128" height="128" :src="cmaster_release.icon_url">
<img :src="cmaster_release.icon_url" height="128" width="128">
</div>
<div class="apps-list">
<div class="known-apps">
@ -39,10 +39,10 @@
</p>
<div class="apps">
<b v-if="comboapplists.length ===0 ">暂无可以合并的应用</b>
<div class="app ng-scope" v-for="comboapp in comboapplists" :key="comboapp.app_id"
@click="each_add(comboapp.app_id)">
<div :key="comboapp.app_id" @click="each_add(comboapp.app_id)" class="app ng-scope"
v-for="comboapp in comboapplists">
<div class="icon">
<img class="ng-isolate-scope" :src="comboapp.master_release|geticon_url">
<img :src="comboapp.master_release|geticon_url" class="ng-isolate-scope">
</div>
<p class="ng-binding">{{ comboapp.name }}</p></div>
</div>
@ -57,11 +57,11 @@
<div class="form-group">
<el-input
@click="searchapps"
clearables
prefix-icon="el-icon-search"
placeholder="输入短链接或者名字"
prefix-icon="el-icon-search"
v-model="searchKey"
@click="searchapps"
/>
</div>
@ -84,14 +84,7 @@
export default {
name: "FirAppInfoscombo"
, mounted() {
this.$store.dispatch('doappInfoIndex', [[31, 31], [31, 31]]);
// this.getappiconFun();
this.setData();
}, data() {
, data() {
return {
searchKey: '',
currentapp: {},
@ -196,9 +189,6 @@
this.getappiconFun();
}
},
findByShort(e) {
alert(111, e)
},
getappiconFun() {
let type = "android";
if (this.currentapp.type === 0) {
@ -227,6 +217,12 @@
}
return ftype
},
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[44, 44], [44, 44]]);
// this.getappiconFun();
this.setData();
},
watch: {
'$store.state.currentapp': function () {

@ -0,0 +1,89 @@
<template>
<div style="margin-top: 20px;width: 90%;margin-left: 8%">
<h2>UDID列表及用户信息</h2>
<el-table
:data="udidlists"
stripe
style="width: 100%">
<el-table-column width="180"
prop="model"
label="设备型号">
</el-table-column>
<el-table-column width="180"
prop="version"
label="系统版本">
</el-table-column>
<el-table-column width="380"
prop="udid"
label="UDID">
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: "FirAppInfosdevices",
data() {
return {
currentapp: {},
udidlists: []
}
},
methods: {
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[57, 57], [57, 57]]);
if(!this.currentapp.app_id){
this.currentapp = this.$store.state.currentapp;
this.udidlists = this.currentapp.master_release.udid;
}
},
watch: {
'$store.state.currentapp': function () {
this.currentapp = this.$store.state.currentapp;
this.udidlists = this.currentapp.master_release.udid;
}
},computed:{
}
}
</script>
<style scoped>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
height: 100px;
width: 100px;
border-radius: 10px;
display: block;
}
</style>

@ -0,0 +1,246 @@
<template>
<div style="margin-top: 20px;width: 56%;margin-left: 8%">
<el-form label-width="80px">
<el-form-item label-width="160px" label="是否下载页显示">
<el-tooltip :content="downtip.msg" placement="top">
<el-switch
@change="showdownloadevent"
v-model="downtip.val"
active-color="#13ce66"
inactive-color="#ff4949"
active-value="on"
inactive-value="off">
</el-switch>
</el-tooltip>
</el-form-item>
<el-form-item label-width="160px" label="是否开启访问密码" >
<el-tooltip placement="top">
<div slot="content">
{{passwordtip.msg}}<br>
<div v-if="passwordtip.val === 'on'">
<el-link icon="el-icon-edit" :underline="false" @click="setaccesspassword">修改</el-link>
</div>
</div>
<el-switch
v-model="passwordtip.val"
@change="showpasswordevent"
active-color="#13ce66"
inactive-color="#ff4949"
active-value="on"
inactive-value="off">
</el-switch>
</el-tooltip>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { updateapp, } from "../restful"
export default {
name: "FirAppInfossecurity",
data() {
return {
currentapp: {},
downtip:{},
passwordtip:{'msg':''},
passwordflag:false,
showdownloadflag:false,
}
},
methods: {
saveappinfo(data) {
updateapp(data => {
if (data.code === 1000) {
this.$message.success('数据更新成功');
}else {
this.$message.error('操作失败,'+data.msg);
}
}, {
"app_id": this.currentapp.app_id,
"data": data
});
},
setbuttondefaltpass(currentapp){
if(currentapp.password === ''){
this.passwordtip.val='off';
this.showpasswordevent("off");
}else {
this.passwordtip.val='on';
this.showpasswordevent("on");
}
this.passwordflag=true;
},
setbuttondefaltshow(currentapp){
if(currentapp.isshow === 1){
this.showdownloadevent("on");
this.downtip.val='on';
}else {
this.showdownloadevent("off");
this.downtip.val='off';
}
this.showdownloadflag=true;
},
setbuttondefault(currentapp){
this.setbuttondefaltpass(currentapp);
this.setbuttondefaltshow(currentapp);
},
passwordswitch(state){
this.passwordflag=false;
this.showpasswordevent(state);
this.passwordtip.val=state;
this.passwordflag=true;
},
setaccesspassword(){
this.$prompt('', '请设置访问密码', {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal:false,
inputValue:`${this.currentapp.password}`,
}).then(({ value }) => {
value = value.replace(/\s+/g,"");
if(this.currentapp.password === value ) {
if(value === ''){
this.$message({
type: 'success',
message: '访问密码未变'
});
this.passwordflag = false;
this.setbuttondefaltpass(this.currentapp);
return
}else {
return
}
}
this.saveappinfo({
"password": value,
});
if(value === ''){
this.passwordswitch("off");
this.$message({
type: 'success',
message: '设置成功,取消密码访问'
});
}else {
this.passwordtip.msg='访问密码:' + value;
this.$message({
type: 'success',
message: '设置成功,访问密码是: ' + value
});
}
this.currentapp.password=value;
this.$store.dispatch('doucurrentapp', this.currentapp)
}).catch(() => {
if(this.currentapp.password === ''){
this.passwordswitch("off")
}
});
},
showdownloadevent(newval){
if(newval === "on"){
if(this.showdownloadflag){
this.saveappinfo({
"isshow": 1,
});
this.currentapp.isshow=1;
}else {
this.downtip.msg='下载页对所有人可见';
}
}else {
if(this.showdownloadflag){
this.saveappinfo({
"isshow": 0,
});
this.currentapp.isshow=0;
}else {
this.downtip.msg = '下载页不可见'
}
}
},
showpasswordevent(newval){
if(newval === "on"){
if(this.passwordflag){
this.setaccesspassword()
}else {
this.passwordtip.msg='访问密码:' + this.currentapp.password ;
}
}else {
if(this.passwordflag){
this.saveappinfo({
"password": '',
});
}
this.currentapp.password='';
this.$store.dispatch('doucurrentapp', this.currentapp);
this.passwordtip.msg='无访问密码'
}
},
appinit(){
this.currentapp = this.$store.state.currentapp;
this.passwordflag=false;
this.showdownloadflag=false;
this.setbuttondefault(this.currentapp);
}
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[31, 31], [31, 31]]);
if(!this.currentapp.app_id){
this.appinit();
}
},
watch: {
'$store.state.currentapp': function () {
this.appinit();
},
},computed:{
}
}
</script>
<style scoped>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
height: 100px;
width: 100px;
border-radius: 10px;
display: block;
}
</style>

@ -58,11 +58,14 @@
</el-button>
<button type="button" v-else-if="wrong">{{ msg }}</button>
<!-- <button v-else @click="download">下载安装</button>-->
<div v-else >
<button v-if="isdownload" disabled="" class="loading" style="min-width: 43px; width: 43px; padding: 12px 0; border-top-color: transparent; border-left-color: transparent;">&nbsp;</button>
<button v-else @click="download" >
<button v-if="isdownload" disabled="" class="loading" style="min-width: 42px; width: 42px; padding: 21px 0; border-top-color: transparent; border-left-color: transparent;">&nbsp;</button>
<div v-else>
<div v-if="currentappinfo.need_password" style="margin:0 auto; width:166px">
<el-input prefix-icon="el-icon-lock" clearable="true" placeholder="请输入密码" v-model="password" icon="el-icon-loadings" type="primary" :underline="false"> </el-input>
</div>
<el-divider v-if="currentappinfo.need_password"></el-divider>
<button @click="download" >
<el-link icon="el-icon-loadings" type="primary" :underline="false"> 下载安装 </el-link>
</button>
</div>
@ -70,6 +73,8 @@
</div>
</div>
</div>
</div>
</div>
</header>
@ -158,12 +163,14 @@
agent: '',
wrong: false,
msg: '',
password:'',
dchoice:false,
downloadurl:"",
isdownload:false,
}
}, methods: {
download() {
if( this.currentappinfo.app_id){
this.isdownload = true;
getdownloadurl(res=>{
if(res.code === 1000){
@ -180,14 +187,24 @@
window.location.href=this.downloadurl;
}
else {
this.isdownload = false;
this.password='';
this.$message({
message:"密码错误,或者下载链接失效",
type: 'error',
});
}
}, {
'data': {
'token': this.mcurrentappinfo.download_token,
'short': this.currentappinfo.short,
'release_id': this.mcurrentappinfo.release_id,
'password':this.password,
},
'app_id': this.currentappinfo.app_id
})
}
},
qrcode() {
new QRCode('qrcode', {

@ -20,6 +20,8 @@ import FirAppBase from "@/components/FirAppBase";
import FirAppInfosBase from "@/components/FirAppInfosBase";
import FirAppInfostimeline from "@/components/FirAppInfostimeline";
import FirAppInfosbaseinfo from "@/components/FirAppInfosbaseinfo";
import FirAppInfossecurity from "@/components/FirAppInfossecurity";
import FirAppInfosdevices from "@/components/FirAppInfosdevices";
import FirAppInfoscombo from "@/components/FirAppInfoscombo";
import FirUserProfile from "@/components/FirUserProfileBase";
import FirUserProfileInfo from "@/components/FirUserProfileInfo";
@ -63,6 +65,16 @@ const router = new VueRouter({
name: 'FirAppInfosbaseinfo',
component: FirAppInfosbaseinfo,
},
{
path: 'security',
name: 'FirAppInfossecurity',
component: FirAppInfossecurity
},
{
path: 'devices',
name: 'FirAppInfosdevices',
component: FirAppInfosdevices
},
{
path: 'combo',
name: 'FirAppInfoscombo',

@ -56,11 +56,14 @@
</button>
<button type="button" v-else-if="wrong">{{ msg }}</button>
<!-- <button v-else @click="download">下载安装</button>-->
<div v-else >
<button v-if="isdownload" disabled="" class="loading" style="min-width: 43px; width: 43px; padding: 12px 0; border-top-color: transparent; border-left-color: transparent;">&nbsp;</button>
<button v-else @click="download" >
<button v-if="isdownload" disabled="" class="loading" style="min-width: 42px; width: 42px; padding: 21px 0; border-top-color: transparent; border-left-color: transparent;">&nbsp;</button>
<div v-else>
<div v-if="currentappinfo.need_password" style="margin:0 auto; width:166px">
<input class="passwd" placeholder="请输入密码" v-model="password" />
</div>
<br/>
<button @click="download" >
<a icon="el-icon-loadings" type="primary" :underline="false"> 下载安装 </a>
</button>
</div>
@ -68,6 +71,8 @@
</div>
</div>
</div>
</div>
</div>
</header>
@ -160,6 +165,7 @@
agent: '',
wrong: false,
msg: '',
password:'',
dchoice:false,
downloadurl:"",
isdownload:false,
@ -167,6 +173,8 @@
}
}, methods: {
download() {
if( this.currentappinfo.app_id) {
this.isdownload = true;
getdownloadurl(res => {
if (res.code === 1000) {
@ -182,15 +190,20 @@
}
window.location.href = this.downloadurl;
} else {
this.isdownload = false;
alert("密码错误,或者下载链接失效")
}
}, {
'data': {
'token': this.mcurrentappinfo.download_token,
'short': this.currentappinfo.short,
'release_id': this.mcurrentappinfo.release_id,
'password': this.password,
},
'app_id': this.currentappinfo.app_id
})
}
},
qrcode(){
new QRCode('qrcode', {
@ -285,11 +298,6 @@
}
else {
// this.$message({
// message: data.msg,
// type: 'error',
// duration:0
// });
this.iserror = true;
}
}, params)
@ -719,38 +727,6 @@
display: none
}
.passwd form {
text-align: center
}
.passwd form h4 {
font-weight: 400;
font-size: 18px
}
.passwd form button, .passwd form input {
padding: 12px 20px;
width: 300px;
border: 1px solid #f8ba0b;
border-radius: 5px;
font-size: 16px
}
.passwd form button:focus, .passwd form input:focus {
outline: 0
}
.passwd form input {
color: #f8ba0b;
text-align: center
}
.passwd form button {
border-color: #f8ba0b;
background-color: #f8ba0b;
color: #fff;
cursor: pointer
}
.main .icon-container {
position: relative;
@ -915,6 +891,14 @@
color: #fff
}
.main > header .actions input {
display: inline-block;
padding: 12px 46px;
width: 166px;
border: 1px solid #32b2a7;
border-radius: 40px;
font-size: 14px;
}
.main > header .actions button a{
color: #fff
}

@ -0,0 +1,28 @@
# Generated by Django 3.0.3 on 2020-04-10 11:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0020_auto_20200407_1157'),
]
operations = [
migrations.AddField(
model_name='apps',
name='password',
field=models.CharField(default='', help_text='默认 没有密码', max_length=32, verbose_name='访问密码'),
),
migrations.AlterField(
model_name='appreleaseinfo',
name='binary_url',
field=models.CharField(blank=True, max_length=128, verbose_name='第三方下载URL'),
),
migrations.AlterField(
model_name='token',
name='access_token',
field=models.CharField(max_length=64, unique=True),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-04-10 17:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0021_auto_20200410_1141'),
]
operations = [
migrations.AddField(
model_name='apps',
name='isshow',
field=models.BigIntegerField(default=1, verbose_name='下载页可见'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-04-11 10:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0022_apps_isshow'),
]
operations = [
migrations.AddField(
model_name='appreleaseinfo',
name='udid',
field=models.TextField(blank=True, default=None, null=True, verbose_name='ios内测版 udid'),
),
]

@ -101,9 +101,10 @@ class Apps(models.Model):
bundle_id = models.CharField(max_length=64,blank=True,verbose_name="bundle id")
has_combo = models.OneToOneField(to="Apps", related_name='combo_app_info',
verbose_name="关联应用",on_delete=models.SET_NULL,null=True,blank=True)
# icon_img = models.ImageField(upload_to="course/%Y-%m", verbose_name='图标')
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
count_hits = models.BigIntegerField(verbose_name="下载次数",default=0)
password = models.CharField(verbose_name="访问密码",default='',help_text='默认 没有密码',max_length=32)
isshow = models.BigIntegerField(verbose_name="下载页可见",default=1)
description = models.TextField('描述', blank=True, null=True, default=None, )
updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
class Meta:
@ -128,6 +129,7 @@ class AppReleaseInfo(models.Model):
binary_url = models.CharField(max_length=128,blank=True,verbose_name="第三方下载URL")
icon_url = models.CharField(max_length=128,blank=True,verbose_name="图标url")
changelog = models.TextField('更新日志', blank=True, null=True, default=None, )
udid = models.TextField('ios内测版 udid', blank=True, null=True, default='', )
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")

@ -151,7 +151,8 @@ def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size):
"minimum_os_version": appinfo.get("miniOSversion", None),
"binary_size": size,
"is_master": True,
"changelog":appinfo.get("changelog",'')
"changelog":appinfo.get("changelog",''),
"udid":appinfo.get("udid",''),
}
AppReleaseInfo.objects.create(**release_data)

@ -64,19 +64,81 @@ class AppsSerializer(serializers.ModelSerializer):
download_token = token_obj.make_token(master_release_obj.release_id,600,key=key)
datainfo["download_token"] = download_token
udid_lists = []
try:
udid_data = eval(master_release_obj.udid)
for udid in udid_data:
udid_lists.append({'udid':udid})
except Exception as e:
pass
datainfo["udid"] = udid_lists
return datainfo
else:
return {}
class AppsShortSerializer(serializers.ModelSerializer):
class Meta:
model = models.Apps
fields = ["app_id","name","short","has_combo","isshow","description","need_password",'master_release']
need_password=serializers.SerializerMethodField()
def get_need_password(self,obj):
if obj.password != '':
return True
return False
has_combo = serializers.SerializerMethodField()
def get_has_combo(self, obj):
if obj.has_combo:
obj.has_combo.has_combo = None
return AppsSerializer(obj.has_combo,context=self.context).data
master_release = serializers.SerializerMethodField()
def get_master_release(self, obj):
master_release_obj = models.AppReleaseInfo.objects.filter(app_id=obj, is_master=True).first()
if self.context.get("release_id", None) and self.context.get("release_id") != "undefined":
master_release_obj = models.AppReleaseInfo.objects.filter(app_id=obj,
release_id=self.context.get("release_id")).first()
if master_release_obj:
icon_url = ""
key=''
if self.context.get("key", None) and self.context.get("key") != "undefined":
key=self.context.get("key", '')
if self.context.get("storage", None) and self.context.get("storage") != "undefined":
storage = self.context.get("storage", None)
icon_url = storage.get_download_url(os.path.basename(master_release_obj.icon_url),600,key=key)
datainfo = {
"app_version": master_release_obj.app_version,
"icon_url": icon_url,
"build_version": master_release_obj.build_version,
"release_type": master_release_obj.release_type,
"created_time": master_release_obj.created_time,
"binary_size": bytes2human(master_release_obj.binary_size),
"release_id": master_release_obj.release_id,
"changelog": master_release_obj.changelog,
"binary_url":master_release_obj.binary_url,
}
download_token = token_obj.make_token(master_release_obj.release_id,600,key=key)
datainfo["download_token"] = download_token
return datainfo
else:
return {}
class AppReleaseSerializer(serializers.ModelSerializer):
class Meta:
model = models.AppReleaseInfo
fields = ["app_version", "icon_url", "build_version",
"release_type", "minimum_os_version",
"created_time", "binary_size", "release_id", "size", "type", "editing", "master_color", "changelog",
"is_master",'download_token','binary_url']
"is_master",'download_token','binary_url','udid']
download_token = serializers.SerializerMethodField()
size = serializers.SerializerMethodField()
type = serializers.SerializerMethodField()
@ -84,8 +146,17 @@ class AppReleaseSerializer(serializers.ModelSerializer):
master_color = serializers.SerializerMethodField()
icon_url = serializers.SerializerMethodField()
binary_size= serializers.SerializerMethodField()
udid= serializers.SerializerMethodField()
def get_udid(self,obj):
udid_lists = []
try:
udid_data = eval(obj.udid)
for udid in udid_data:
udid_lists.append({'udid': udid})
except Exception as e:
pass
return udid_lists
def get_binary_size(self,obj):
return bytes2human(obj.binary_size)

@ -5,8 +5,8 @@
# date: 2020/4/7
from django.core.cache import cache
from api.models import Apps,UserInfo
import time
from api.models import Apps,UserInfo,AppReleaseInfo
import time,os
from django.utils import timezone
from fir_ser.settings import CACHE_KEY_TEMPLATE
from api.utils.storage.storage import Storage,LocalStorage
@ -29,12 +29,22 @@ def get_download_url_by_cache(app_obj, filename, limit, isdownload=True,key=''):
return storage.get_download_url(filename, limit)
def get_app_instance_by_cache(app_id, limit):
def get_app_instance_by_cache(app_id,password, limit):
app_key="_".join([CACHE_KEY_TEMPLATE.get("app_instance_key"),app_id])
app_obj_cache = cache.get(app_key)
if not app_obj_cache:
app_obj_cache = Apps.objects.filter(app_id=app_id).values("pk", 'user_id', 'type').first()
app_obj_cache = Apps.objects.filter(app_id=app_id).values("pk", 'user_id', 'type','password').first()
cache.set(app_key, app_obj_cache, limit)
app_password=app_obj_cache.get("password")
if app_password != '':
if password is None:
return None
if app_password.lower() != password.strip().lower():
return None
return app_obj_cache
@ -55,6 +65,14 @@ def del_cache_response_by_short(short,app_id):
cache.delete("_".join([CACHE_KEY_TEMPLATE.get("app_instance_key"),app_id]))
key='ShortDownloadView'.lower()
master_release_dict = AppReleaseInfo.objects.filter(app_id__app_id=app_id,is_master=True).values('icon_url','release_id').first()
download_val = CACHE_KEY_TEMPLATE.get('download_url_key')
cache.delete("_".join([key, download_val, os.path.basename(master_release_dict.get("icon_url"))]))
cache.delete("_".join([key,download_val, master_release_dict.get('release_id')]))
cache.delete("_".join([key.lower(), CACHE_KEY_TEMPLATE.get("make_token_key"), master_release_dict.get('release_id')]))
def set_app_today_download_times(app_id):
now = timezone.now()
down_tem_key = "_".join([CACHE_KEY_TEMPLATE.get("download_today_times_key"),

@ -167,6 +167,8 @@ class AppInfoView(APIView):
del_cache_response_by_short(apps_obj.short,apps_obj.app_id)
apps_obj.short = data.get("short", apps_obj.short)
apps_obj.name = data.get("name", apps_obj.name)
apps_obj.password = data.get("password", apps_obj.password)
apps_obj.isshow = data.get("isshow", apps_obj.isshow)
apps_obj.save()
except Exception as e:
res.code = 1005

@ -15,7 +15,7 @@ from api.utils.storage.caches import get_app_instance_by_cache,get_download_url_
import os
from rest_framework_extensions.cache.decorators import cache_response
from api.utils.serializer import AppsSerializer
from api.utils.serializer import AppsShortSerializer
from api.models import Apps,AppReleaseInfo
from django.http import FileResponse
class DownloadView(APIView):
@ -81,7 +81,12 @@ class ShortDownloadView(APIView):
res.msg="该应用不存在"
return Response(res.dict)
app_serializer = AppsSerializer(app_obj,context={"key":"ShortDownloadView","release_id":release_id,"storage":Storage(app_obj.user_id)})
if not app_obj.isshow:
res.code=1004
res.msg="您没有权限访问该应用"
return Response(res.dict)
app_serializer = AppsShortSerializer(app_obj,context={"key":"ShortDownloadView","release_id":release_id,"storage":Storage(app_obj.user_id)})
res.data = app_serializer.data
return Response(res.dict)
@ -101,6 +106,7 @@ class InstallView(APIView):
short = request.query_params.get("short",None)
release_id = request.query_params.get("release_id",None)
isdownload = request.query_params.get("isdownload",None)
password = request.query_params.get("password",None)
if not downtoken or not short or not release_id:
res.code=1004
@ -109,15 +115,9 @@ class InstallView(APIView):
dtoken = DownloadToken()
if dtoken.verify_token(downtoken,release_id):
# app_obj = Apps.objects.filter(app_id=app_id).values("pk",'user_id','type','short').first()
app_obj = get_app_instance_by_cache(app_id,900)
app_obj = get_app_instance_by_cache(app_id,password,900)
if app_obj:
# Apps.objects.filter(app_id=app_id).update(count_hits=F('count_hits') + 1)
# UserInfo.objects.filter(pk=app_obj.get("user_id")).update(all_download_times=F('all_download_times') + 1)
set_app_download_by_cache(app_id)
# release_obj = AppReleaseInfo.objects.filter(app_id=app_obj.get('pk')).values("release_id")
# if release_obj:
if app_obj.get("type") == 0:
apptype = '.apk'
download_url = get_download_url_by_cache(app_obj,release_id + apptype,600)

@ -85,7 +85,8 @@ class AppAnalyseView(APIView):
"versioncode":data.get("version"),
"release_type":data.get("release_type"),
"miniOSversion":data.get("miniosversion"),
"changelog":data.get("changelog",'')
"changelog":data.get("changelog",''),
"udid": data.get("udid", ''),
}
try:

Loading…
Cancel
Save