add qiniu storage

storage_cloud
nineven 5 years ago
parent de55f72941
commit a20d3c7dff
  1. 4
      fir_client/package.json
  2. BIN
      fir_client/src/assets/imgs/1.png
  3. BIN
      fir_client/src/assets/imgs/2.png
  4. BIN
      fir_client/src/assets/imgs/3.png
  5. BIN
      fir_client/src/assets/imgs/5.png
  6. BIN
      fir_client/src/assets/imgs/6.png
  7. BIN
      fir_client/src/assets/imgs/7.png
  8. BIN
      fir_client/src/assets/imgs/8.png
  9. BIN
      fir_client/src/assets/imgs/9.png
  10. 397
      fir_client/src/components/FirApps.vue
  11. 2
      fir_client/src/main.js
  12. 16
      fir_client/src/restful/index.js
  13. 86
      fir_client/src/utils/index.js
  14. 141
      fir_client/yarn.lock
  15. 31
      fir_ser/api/urls.py
  16. 82
      fir_ser/api/utils/app/analyze.py
  17. 49
      fir_ser/api/utils/qiniu/tools.py
  18. 23
      fir_ser/api/utils/serializer.py
  19. 2
      fir_ser/api/views/localuploads.py
  20. 78
      fir_ser/api/views/qiniuuploads.py

@ -1,5 +1,5 @@
{
"name": "fir_test",
"name": "fir_client",
"version": "0.1.0",
"private": true,
"scripts": {
@ -8,10 +8,12 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"app-info-parser": "^0.3.9",
"axios": "^0.19.0",
"core-js": "^3.4.4",
"element-ui": "^2.13.0",
"js-base64": "^2.5.2",
"qiniu-js": "^2.5.5",
"qrcodejs2": "^0.0.2",
"vue": "^2.6.10",
"vue-cookies": "^1.7.0",

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

@ -7,13 +7,158 @@
<el-dialog
:title="getDelappTitle"
:visible.sync="willDeleteApp"
width="50%"
>
width="50%">
<span>删除后不可恢复请谨慎操作</span>
<span slot="footer" class="dialog-footer">
<el-button @click="willDeleteApp = false"> </el-button>
<el-button type="danger" @click="delApp"> </el-button>
</span>
<el-button @click="willDeleteApp = false"> </el-button>
<el-button type="danger" @click="delApp"> </el-button>
</span>
</el-dialog>
<el-dialog class="upload-app"
:visible.sync="willuploadApp"
:destroy-on-close="true"
:show-close="!uploading"
width="40%"
:close-on-click-modal="false"
@closed="closeUpload" >
<div v-if="!uploading" style="">
<el-row :gutter="20">
<el-col :span="6">
<div class="grid-content bg-purple">
<div style="width: 100px;height: 100px">
<el-avatar shape="square" :size="100" :src="analyseappinfo.icon"></el-avatar>
</div>
</div>
</el-col>
<el-col :span="18">
<div class="grid-content bg-purple">
<el-row :gutter="20" style="margin-top: 8px;">
<el-col :span="18" >
{{ analyseappinfo.version}} (Build {{ analyseappinfo.buildversion}}) {{ analyseappinfo.release_type_id|getiOStype}}
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 18px;">
<el-col :span="18" >
<el-input v-model="analyseappinfo.appname"></el-input>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
<el-divider></el-divider>
<el-row :gutter="20">
<el-col :span="6">
<div class="grid-content bg-purple">
<el-row :gutter="20" style="margin-top: 18px;">
<el-col :span="18" :offset="8">
<span >短连接</span>
</el-col>
</el-row>
</div>
</el-col>
<el-col :span="18">
<div class="grid-content bg-purple">
<el-row :gutter="20" style="margin-top: 10px;">
<el-col :span="18" >
<el-input v-model="analyseappinfo.short">
<template slot="prepend">{{analyseappinfo.domain_name}}/</template>
</el-input>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="6">
<div class="grid-content bg-purple">
<el-row :gutter="20" style="margin-top: 18px;">
<el-col :span="18" :offset="8">
<span >更新日志</span>
</el-col>
</el-row>
</div>
</el-col>
<el-col :span="18">
<div class="grid-content bg-purple">
<el-row :gutter="20" style="margin-top: 10px;">
<el-col :span="18" >
<el-input type="textarea"
v-model="analyseappinfo.changelog"
placeholder="请输入内容"
rows="5"
show-word-limit></el-input>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
</div>
<div v-if="uploading" class="wrap">
<!--包裹所有元素的容器-->
<div class="cube">
<!--前面图片 -->
<div class="out_front">
<img src="../assets/imgs/1.png" class="pic">
</div>
<!--后面图片 -->
<div class="out_back">
<img src="../assets/imgs/5.png" class="pic">
</div>
<!--左面图片 -->
<div class="out_left">
<img src="../assets/imgs/6.png" class="pic">
</div>
<!--右面图片 -->
<div class="out_right">
<img src="../assets/imgs/7.png" class="pic">
</div>
<!--上面图片 -->
<div class="out_top">
<img src="../assets/imgs/8.png" class="pic">
</div>
<!--下面图片 -->
<div class="out_bottom">
<img src="../assets/imgs/9.png" class="pic">
</div>
<!--小正方体 -->
<span class="in_front">
<img :src="this.analyseappinfo.icon" class="in_pic">
</span>
<span class="in_back">
<img :src="this.analyseappinfo.icon" class="in_pic">
</span>
<span class="in_left">
<img :src="this.analyseappinfo.icon" class="in_pic">
</span>
<span class="in_right">
<img :src="this.analyseappinfo.icon" class="in_pic">
</span>
<span class="in_top">
<img :src="this.analyseappinfo.icon" class="in_pic">
</span>
<span class="in_bottom">
<img :src="this.analyseappinfo.icon" class="in_pic">
</span>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-progress :text-inside="true" :stroke-width="26" :percentage="uploadprocess" v-if="uploadflag === true"></el-progress>
<el-button type="primary" plain @click="uploadcloud" v-else>开始上传</el-button>
</span>
</el-dialog>
<el-row>
@ -112,7 +257,6 @@
</el-row>
</el-header>
@ -124,12 +268,16 @@
<el-col style="width: 33%;height: 460px ">
<div class=" app-animator appdownload">
<div class=" card app card-ios" style="padding: 0">
<!-- :action="uploadconf.UploadUrl"-->
<el-upload
:on-success="handleAvataruploadsuccess"
drag
:show-file-list="false"
:before-upload="beforeAvatarUpload"
accept=".ipa , .apk"
:headers="uploadconf.AuthHeaders"
:action="uploadconf.UploadUrl"
action="#"
multiple>
<i class="el-icon-upload" style="color: #fff"></i>
<div class="el-upload__text" style="color: #fff;margin-top: 20px">拖拽到这里上传</div>
@ -227,20 +375,23 @@
</template>
<script>
import {getapps, deleteapp} from "../restful";
import {getScrollHeight,getScrollTop,getWindowHeight} from "../utils";
import {getapps, deleteapp,getuploadToken} from "../restful";
import {getScrollHeight,getScrollTop,getWindowHeight,getappinfo,uploadqiniuoss,dataURLtoFile} from "../utils";
export default {
name: "FirApps",
data() {
return {
uploadconf: {"UploadUrl": ""},
analyseappinfo:{'appname':'','short':'','changelog':''},
keysearch: '',
searchfromtype: '',
applists: [],
orgapplists: [],
hdata: {},
willDeleteApp: false,
willuploadApp:false,
uploading:false,
delapp: {},
has_next:false,
query:{'page':1,size:20},
@ -248,8 +399,70 @@
uploadflag:false,
autoloadflag:true,
firstloadflag:true,
currentfile:null,
uploadprocess:0,
uploadsuccess:0
}
}, methods: {
uploadqiniu(file,certinfo){
// eslint-disable-next-line no-unused-vars
uploadqiniuoss(file,certinfo,this,res=>{
this.uploadsuccess +=1;
if(this.uploadsuccess === 2){
this.$message.success(file.name + '上传成功');
getuploadToken(data => {
if (data.code === 1000) {
let app_uuid = this.analyseappinfo.app_uuid;
this.closeUpload();
// app.getappsFun({});
this.$router.push({name: 'FirAppInfostimeline', params: {id: app_uuid}});
}
},{'methods':'PUT','data':this.analyseappinfo});
}
},process=>{
if(this.uploadsuccess === 1) {
this.uploadprocess = process;
}
})
},
getuploadtoken(){
getuploadToken(data =>{
if(data.code === 1000){
this.analyseappinfo.short = data.data.short;
this.analyseappinfo.domain_name = data.data.domain_name;
this.analyseappinfo.app_uuid= data.data.app_uuid;
this.analyseappinfo.upload_token= data.data.upload_token;
this.analyseappinfo.upload_key= data.data.upload_key;
this.analyseappinfo.png_key= data.data.png_key;
this.analyseappinfo.png_token= data.data.png_token;
this.willuploadApp=true;
}else {
this.$message.error("上传token获取失败,请刷新重试")
}
},{'methos':true,'data':{"bundleid":this.analyseappinfo.bundleid,"type":this.analyseappinfo.type}})
},
uploadcloud(){
this.uploadflag=true;
this.uploading = true;
let file=dataURLtoFile(this.analyseappinfo.icon,this.analyseappinfo.png_key);
this.uploadqiniu(file,{'upload_key':this.analyseappinfo.png_key,'upload_token':this.analyseappinfo.png_token});
file = this.currentfile;
this.uploadqiniu(file,{'upload_key':this.analyseappinfo.upload_key,'upload_token':this.analyseappinfo.upload_token});
},
closeUpload(){
this.uploadsuccess = 0;
this.uploadprocess=0;
this.uploadflag=false;
this.uploading = false;
this.willuploadApp=false;
this.currentfile=null;
this.uploadflag = false;
this.analyseappinfo={};
},
searchFun(){
let keysearch = this.keysearch.replace(/^\s+|\s+$/g, "");
if(keysearch === ''){
@ -371,13 +584,33 @@
this.uploadflag = true;
this.getappsFun({});
},beforeAvatarUpload(file){
const loading = this.$loading({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)'
});
getappinfo(file,appinfo =>{
if(appinfo.bundleid){
this.analyseappinfo = appinfo;
this.getuploadtoken();
this.currentfile = file;
}else {
this.$message.error("应用解析失败,请检查是否为APP应用")
}
loading.close();
},err=>{
loading.close();
this.$message.error("应用解析失败,请检查是否为APP应用");
// eslint-disable-next-line no-console
console.log('err ----> ', err);
});
return false;
},
delApp() {
this.willDeleteApp = false;
// eslint-disable-next-line no-console
deleteapp(data => {
if (data.code === 1000) {
for(let i=0;i< this.applists.length;i++){
@ -390,7 +623,6 @@
this.delapp={};
} else {
this.$message.error('删除失败,请联系管理员');
}
}, {
"app_id": this.delapp.app_id
@ -408,8 +640,6 @@
this.$router.push({name: 'FirDownload', params: { short: app.short }})
}
}, computed: {
getDelappTitle() {
return `删除应用 ${this.delapp.name}`
},
@ -424,10 +654,8 @@
}
return sch;
}
},
filters: {
getiOStype: function (type) {
let ftype = '';
if (type === 1) {
@ -443,6 +671,8 @@
autoformat: function (packname) {
if ((packname.length) > 20) {
return packname.split('').slice(0, 20).join('') + '...';
}else {
return packname
}
},
getapptype: function (type) {
@ -466,8 +696,6 @@
this.$store.dispatch('doucurrentapp', {});
this.getappsFun({});
// window.addEventListener('scroll',this.auto_load);
},
destroyed(){
window.removeEventListener('scroll', this.auto_load, false);
@ -478,7 +706,6 @@
keysearch: function (val, oldVal) {
// this.searchapps()
this.searchFun()
},
// eslint-disable-next-line no-unused-vars
searchfromtype: function (val, oldVal) {
@ -486,7 +713,6 @@
this.query.page=1;
// this.keysearch='';
this.searchFun();
// this.getappsFun({"type": this.searchfromtype});
},
}
@ -495,7 +721,6 @@
<style scoped>
.el-container {
margin: 10px auto 100px;
width: 1166px;
@ -771,5 +996,133 @@
color: #4a4a4a
}
/*最外层容器样式*/
.wrap {
width: 100px;
height: 100px;
/*margin: 119px;*/
margin: 120px auto 119px auto;
position: relative;
}
/*包裹所有容器样式*/
.cube {
width: 50px;
height: 50px;
margin: 0 auto;
transform-style: preserve-3d;
transform: rotateX(-30deg) rotateY(-80deg);
animation: rotate linear 20s infinite;
}
@-webkit-keyframes rotate {
from {
transform: rotateX(0deg) rotateY(0deg);
}
to {
transform: rotateX(360deg) rotateY(360deg);
}
}
.cube div {
position: absolute;
width: 200px;
height: 200px;
opacity: 0.8;
transition: all .4s;
}
/*定义所有图片样式*/
.pic {
width: 200px;
height: 200px;
}
.cube .out_front {
transform: rotateY(0deg) translateZ(100px);
}
.cube .out_back {
transform: translateZ(-100px) rotateY(180deg);
}
.cube .out_left {
transform: rotateY(-90deg) translateZ(100px);
}
.cube .out_right {
transform: rotateY(90deg) translateZ(100px);
}
.cube .out_top {
transform: rotateX(90deg) translateZ(100px);
}
.cube .out_bottom {
transform: rotateX(-90deg) translateZ(100px);
}
/*定义小正方体样式*/
.cube span {
display: block;
width: 100px;
height: 100px;
position: absolute;
top: 50px;
left: 50px;
}
.cube .in_pic {
width: 100px;
height: 100px;
}
.cube .in_front {
transform: rotateY(0deg) translateZ(50px);
}
.cube .in_back {
transform: translateZ(-50px) rotateY(180deg);
}
.cube .in_left {
transform: rotateY(-90deg) translateZ(50px);
}
.cube .in_right {
transform: rotateY(90deg) translateZ(50px);
}
.cube .in_top {
transform: rotateX(90deg) translateZ(50px);
}
.cube .in_bottom {
transform: rotateX(-90deg) translateZ(50px);
}
/*鼠标移入后样式*/
.cube:hover .out_front {
transform: rotateY(0deg) translateZ(200px);
}
.cube:hover .out_back {
transform: translateZ(-200px) rotateY(180deg);
}
.cube:hover .out_left {
transform: rotateY(-90deg) translateZ(200px);
}
.cube:hover .out_right {
transform: rotateY(90deg) translateZ(200px);
}
.cube:hover .out_top {
transform: rotateX(90deg) translateZ(200px);
}
.cube:hover .out_bottom {
transform: rotateX(-90deg) translateZ(200px);
}
</style>

@ -4,6 +4,8 @@ import router from "@/router";
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import Vuex from 'vuex'
const qiniu = require('qiniu-js');
Vue.prototype.qiniu = qiniu;
//使用vue-cookies
import VueCookies from 'vue-cookies'

@ -580,3 +580,19 @@ export function getuserpicurl() {
export function getapppicurl(app_id) {
return USERSEVER + '/apps/' + app_id
}
/**获取上传token */
export function getuploadToken(callBack, params, load = true) {
getData(
params.methods,
USERSEVER + '/qiniuupload' ,
params.data,
data => {
callBack(data);
},
load,
true,
true
);
}

@ -1,4 +1,5 @@
//滚动条在Y轴上的滚动距离
export function getScrollTop(){
let scrollTop = 0, bodyScrollTop = 0, documentScrollTop = 0;
if(document.body){
@ -39,3 +40,88 @@ export function getWindowHeight(){
}
return windowHeight;
}
export function getappinfo(file,successcallback,errcallback) {
const AppInfoParser = require('app-info-parser');
const parser = new AppInfoParser(file);
let analyseappinfo={};
parser.parse().then(result => {
analyseappinfo.icon = result.icon;
analyseappinfo.filename = file.name;
analyseappinfo.filesize = file.size;
if(result.CFBundleDisplayName){
analyseappinfo.appname = result.CFBundleDisplayName;
analyseappinfo.bundleid = result.CFBundleIdentifier;
analyseappinfo.version = result.CFBundleShortVersionString;
analyseappinfo.buildversion = result.CFBundleVersion;
analyseappinfo.miniosversion = result.MinimumOSVersion;
if(result.mobileProvision.ProvisionedDevices){
analyseappinfo.release_type='Adhoc';
analyseappinfo.release_type_id=1;
analyseappinfo.udid=result.mobileProvision.ProvisionedDevices;
}else {
analyseappinfo.release_type='Inhouse';
analyseappinfo.release_type_id=2;
analyseappinfo.udid=[];
}
analyseappinfo.type = 'iOS';
}else {
analyseappinfo.appname = result.application.label[0];
analyseappinfo.bundleid = result.package;
analyseappinfo.version = result.versionName;
analyseappinfo.buildversion = result.versionCode;
analyseappinfo.miniosversion = result.usesSdk.minSdkVersion;
analyseappinfo.type = 'Android';
analyseappinfo.release_type = 'Android';
analyseappinfo.release_type_id=0;
}
successcallback(analyseappinfo)
}).catch(err => {
errcallback(err);
});
}
export function uploadqiniuoss(file,certinfo,app,successcallback,processcallback){
// let app = this;
// let file=this.currentfile;
let observer = {
next(res){
processcallback(Math.round(res.total.percent));
},
error(err){
// eslint-disable-next-line no-console
console.log(err);
app.$message({
message:file.name + '上传失败,请刷新页面重试',
type: 'error',
duration:0
});
},
complete(res){
// eslint-disable-next-line no-console
// console.log(res);
successcallback(res);
}
};
let putExtra = {
fname: file.name,
params: {},
// mimeType: ["image/png"]
};
let config={};
let observable = app.qiniu.upload(file, certinfo.upload_key, certinfo.upload_token, putExtra, config);
// eslint-disable-next-line no-unused-vars
let subscription = observable.subscribe(observer) // 上传开始
// subscription.unsubscribe() // 上传取消
}
export function dataURLtoFile(dataurl, filename) {//将base64转换为文件
let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {type:mime});
}

@ -1322,6 +1322,17 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
app-info-parser@^0.3.9:
version "0.3.9"
resolved "https://registry.yarnpkg.com/app-info-parser/-/app-info-parser-0.3.9.tgz#adc60c9d4d4aca4e529f21a6da1279b74ddaf62b"
integrity sha512-vIzO1OZSMnEz+OdGkhmSTS0y4PaQmXdF1YmzUBb7wsKR21JF1eXMnciARkUj6T+CLb9caoDSN+w5eSoGatRbcA==
dependencies:
bplist-parser "^0.2.0"
bytebuffer "^5.0.1"
cgbi-to-png "^1.0.7"
isomorphic-unzip "^1.1.5"
plist "^3.0.1"
aproba@^1.1.1:
version "1.2.0"
resolved "https://registry.npm.taobao.org/aproba/download/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
@ -1532,7 +1543,7 @@ balanced-match@^1.0.0:
resolved "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
base64-js@^1.0.2:
base64-js@^1.0.2, base64-js@^1.2.3:
version "1.3.1"
resolved "https://registry.npm.taobao.org/base64-js/download/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
integrity sha1-WOzoy3XdB+ce0IxzarxfrE2/jfE=
@ -1572,6 +1583,11 @@ bfj@^6.1.1:
hoopy "^0.1.4"
tryer "^1.0.1"
big-integer@^1.6.44:
version "1.6.48"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==
big.js@^3.1.3:
version "3.2.0"
resolved "https://registry.npm.taobao.org/big.js/download/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
@ -1637,6 +1653,13 @@ boolbase@^1.0.0, boolbase@~1.0.0:
resolved "https://registry.npm.taobao.org/boolbase/download/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
bplist-parser@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e"
integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==
dependencies:
big-integer "^1.6.44"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@ -1734,6 +1757,11 @@ browserslist@^4.0.0, browserslist@^4.6.0, browserslist@^4.7.3, browserslist@^4.8
electron-to-chromium "^1.3.322"
node-releases "^1.1.44"
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.npm.taobao.org/buffer-from/download/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
@ -1763,11 +1791,31 @@ buffer@^4.3.0:
ieee754 "^1.1.4"
isarray "^1.0.0"
buffer@^5.0.7, buffer@^5.1.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.5.0.tgz#9c3caa3d623c33dd1c7ef584b89b88bf9c9bc1ce"
integrity sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww==
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
bufferpack@0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/bufferpack/-/bufferpack-0.0.6.tgz#fb3d8738a0e1e4e03bcff99f9a75f9ec18a9d73e"
integrity sha1-+z2HOKDh5OA7z/mfmnX57Bip1z4=
builtin-status-codes@^3.0.0:
version "3.0.0"
resolved "https://registry.npm.taobao.org/builtin-status-codes/download/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
bytebuffer@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd"
integrity sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=
dependencies:
long "~3"
bytes@3.0.0:
version "3.0.0"
resolved "https://registry.npm.taobao.org/bytes/download/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
@ -1917,6 +1965,16 @@ caseless@~0.12.0:
resolved "https://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
cgbi-to-png@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/cgbi-to-png/-/cgbi-to-png-1.0.7.tgz#c7497580f76f87c2f5d825748a9d902b4072c004"
integrity sha512-YR80kxTPuq9oRpZUdQmNEQWrmTKLINk1cfLVfyrV7Rfr9KLtLJdcockPKbreIr4JYAq+DhHBR7w+WA/tF5VDaQ==
dependencies:
bufferpack "0.0.6"
crc "^3.3.0"
stream-to-buffer "^0.1.0"
streamifier "^0.1.1"
chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz?cache=0&sync_timestamp=1573614700252&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@ -2338,6 +2396,13 @@ cosmiconfig@^5.0.0:
js-yaml "^3.13.1"
parse-json "^4.0.0"
crc@^3.3.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6"
integrity sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==
dependencies:
buffer "^5.1.0"
create-ecdh@^4.0.0:
version "4.0.3"
resolved "https://registry.npm.taobao.org/create-ecdh/download/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
@ -3384,6 +3449,13 @@ faye-websocket@~0.11.1:
dependencies:
websocket-driver ">=0.5.1"
fd-slicer@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
dependencies:
pend "~1.2.0"
figgy-pudding@^3.5.1:
version "3.5.1"
resolved "https://registry.npm.taobao.org/figgy-pudding/download/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
@ -4508,6 +4580,14 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.npm.taobao.org/isobject/download/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
isomorphic-unzip@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/isomorphic-unzip/-/isomorphic-unzip-1.1.5.tgz#9e5a18e77e3e760b631ee451f643c784b4f880dd"
integrity sha512-2McA51lWhmO3Kk438jxVcYeh6L+AOqVnl9XdX1yI7GlLA9RwEyTBgGem1rNuRIU2abAmOiv+IagThdUxASY4IA==
dependencies:
buffer "^5.0.7"
yauzl "^2.8.0"
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.npm.taobao.org/isstream/download/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@ -4800,6 +4880,11 @@ loglevel@^1.6.6:
resolved "https://registry.npm.taobao.org/loglevel/download/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312"
integrity sha1-DuYwDMBY22s1UfocS/c7g7t3ExI=
long@~3:
version "3.2.0"
resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"
integrity sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=
loose-envify@^1.0.0:
version "1.4.0"
resolved "https://registry.npm.taobao.org/loose-envify/download/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
@ -5749,6 +5834,11 @@ pbkdf2@^3.0.3:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.npm.taobao.org/performance-now/download/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
@ -5802,6 +5892,15 @@ pkg-dir@^4.1.0:
dependencies:
find-up "^4.0.0"
plist@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c"
integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==
dependencies:
base64-js "^1.2.3"
xmlbuilder "^9.0.7"
xmldom "0.1.x"
portfinder@^1.0.25:
version "1.0.25"
resolved "https://registry.npm.taobao.org/portfinder/download/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca"
@ -6289,6 +6388,11 @@ q@^1.1.2:
resolved "https://registry.npm.taobao.org/q/download/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
qiniu-js@^2.5.5:
version "2.5.5"
resolved "https://registry.yarnpkg.com/qiniu-js/-/qiniu-js-2.5.5.tgz#77d295f222620f9377d6148f3f757d189a1e4977"
integrity sha512-cJcdZMCfU4OJirjCzKJwPXSgrXF0INB2qtqA6ImNBe2PJFqi8kWE7rp3qr1RKH0H4OTtPOXyBC2FVSl/zM7ERg==
qrcodejs2@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/qrcodejs2/-/qrcodejs2-0.0.2.tgz#465afe5e39f19facecb932c11f7a186109146ae1"
@ -7133,6 +7237,23 @@ stream-shift@^1.0.0:
resolved "https://registry.npm.taobao.org/stream-shift/download/stream-shift-1.0.1.tgz?cache=0&sync_timestamp=1576147178936&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstream-shift%2Fdownload%2Fstream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha1-1wiCgVWasneEJCebCHfaPDktWj0=
stream-to-buffer@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz#26799d903ab2025c9bd550ac47171b00f8dd80a9"
integrity sha1-JnmdkDqyAlyb1VCsRxcbAPjdgKk=
dependencies:
stream-to "~0.2.0"
stream-to@~0.2.0:
version "0.2.2"
resolved "https://registry.yarnpkg.com/stream-to/-/stream-to-0.2.2.tgz#84306098d85fdb990b9fa300b1b3ccf55e8ef01d"
integrity sha1-hDBgmNhf25kLn6MAsbPM9V6O8B0=
streamifier@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/streamifier/-/streamifier-0.1.1.tgz#97e98d8fa4d105d62a2691d1dc07e820db8dfc4f"
integrity sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8=
strict-uri-encode@^1.0.0:
version "1.1.0"
resolved "https://registry.npm.taobao.org/strict-uri-encode/download/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
@ -8084,6 +8205,16 @@ ws@^6.0.0, ws@^6.2.1:
dependencies:
async-limiter "~1.0.0"
xmlbuilder@^9.0.7:
version "9.0.7"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
xmldom@0.1.x:
version "0.1.31"
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff"
integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==
xtend@^4.0.0, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.npm.taobao.org/xtend/download/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
@ -8160,6 +8291,14 @@ yargs@^15.0.0:
y18n "^4.0.0"
yargs-parser "^16.1.0"
yauzl@^2.8.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"
yorkie@^2.0.0:
version "2.0.0"
resolved "https://registry.npm.taobao.org/yorkie/download/yorkie-2.0.0.tgz#92411912d435214e12c51c2ae1093e54b6bb83d9"

@ -15,14 +15,14 @@ Including another URLconf
"""
from django.contrib import admin
from django.urls import path,re_path,include
from rest_framework.routers import DefaultRouter
# from api.views.course import CourseView,CourseDetailView,CourseCategoryView
from api.views.login import LoginView,UserInfoView
from api.views.logout import LogoutView
from api.views.captcha import CaptchaView
from api.views.apps import AppsView,AppinfoView,AppReleaseinfoView
from api.views.uploads import UploadView,UploadImgView
from api.views.download import DownloadView,DownloadTokenView
from api.views.localuploads import UploadView,UploadImgView
from api.views.download import DownloadTokenView
from api.views.qiniuuploads import QiNiuUploadView
# router=DefaultRouter()
@ -30,16 +30,15 @@ from api.views.download import DownloadView,DownloadTokenView
urlpatterns = [
# path("",include(router.urls)),
re_path("login",LoginView.as_view()),
re_path("logout",LogoutView.as_view()),
re_path("captcha_check/",CaptchaView.as_view()),
re_path("apps$", AppsView.as_view()),
re_path("apps/(?P<app_id>\w+)", AppinfoView.as_view()),
re_path("appinfos/(?P<app_id>\w+)/(?P<act>\w+)", AppReleaseinfoView.as_view()),
re_path("upload$",UploadView.as_view()),
re_path("img/upload$",UploadImgView.as_view()),
re_path("userinfo",UserInfoView.as_view()),
# re_path("download/(?P<release_id>\w+)$", DownloadView.as_view()),
re_path("download_token/(?P<short>\w+)$", DownloadTokenView.as_view()),
re_path("^login",LoginView.as_view()),
re_path("^logout",LogoutView.as_view()),
re_path("^captcha_check/",CaptchaView.as_view()),
re_path("^apps$", AppsView.as_view()),
re_path("^apps/(?P<app_id>\w+)", AppinfoView.as_view()),
re_path("^appinfos/(?P<app_id>\w+)/(?P<act>\w+)", AppReleaseinfoView.as_view()),
re_path("^upload$",UploadView.as_view()),
re_path("^img/upload$",UploadImgView.as_view()),
re_path("^userinfo",UserInfoView.as_view()),
re_path("^download_token/(?P<short>\w+)$", DownloadTokenView.as_view()),
re_path("^qiniuupload$", QiNiuUploadView.as_view()),
]

@ -10,6 +10,7 @@ from api.utils.randomstrings import make_app_uuid
from api.models import AppReleaseInfo,Apps
import random,xmltodict,json
from .TokenManager import DownloadToken
from api.utils.qiniu.tools import QiNiu
def make_download_token(release_id,time_limit):
@ -118,19 +119,26 @@ def delete_apps_storage(app_file_name,release_type):
else:
app_file_full_name="%s.ipa" %app_file_name
qiniu = QiNiu()
qiniu.del_qiniu_file(app_file_full_name)
icon_url = AppReleaseInfo.objects.filter(release_id=app_file_name).first().icon_url
qiniu.del_qiniu_file(os.path.basename(icon_url))
remove_lists=[
os.path.join(app_path, app_file_full_name),
os.path.join(icon_path, "%s.png" % (app_file_name))
]
for delfiles in remove_lists:
os.remove(delfiles)
except Exception as e:
print(e)
def delete_apps_icon_storage(app_file_name,type='icons'):
icon_path = os.path.join(MEDIA_ROOT,type)
qiniu = QiNiu()
qiniu.del_qiniu_file(app_file_name)
try:
os.remove(os.path.join(icon_path, app_file_name))
except Exception as e:
@ -184,55 +192,61 @@ def AnalyzeUtil(app_file_name,request):
apiobj = AppInfo(app_full_path)
appinfo = apiobj.get_app_data()
app_img=apiobj.make_app_png(icon_path,app_file_name.split(".")[0])
bundle_id = appinfo.get("bundle_id")
SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,get_app_type(app_file_name),os.path.getsize(app_full_path))
except Exception as e:
print(e)
delete_apps_storage(app_file_name.split(".")[0], 0)
delete_apps_storage(app_file_name.split(".")[0], 1)
return
bundle_id = appinfo.get("bundle_id")
def SaveAppInfos(app_file_name,user_obj,appinfo,bundle_id,app_img,short,size):
app_uuid = make_app_uuid(user_obj,bundle_id+app_file_name.split(".")[1])
##判断是否存在该app
appmobj=Apps.objects.filter(app_id=app_uuid,user_id=user_obj).first()
appmobj = Apps.objects.filter(app_id=app_uuid, user_id=user_obj).first()
if not appmobj:
appdata={
"app_id":app_uuid,
"user_id":user_obj,
"type":get_app_type(app_file_name),
appdata = {
"app_id": app_uuid,
"user_id": user_obj,
"type": get_app_type(app_file_name),
"name": appinfo["labelname"],
"short":get_random_short(),
"bundle_id":bundle_id,
"count_hits":0
"short": short,
"bundle_id": bundle_id,
"count_hits": 0
}
try:
appmobj=Apps.objects.create(**appdata)
appmobj = Apps.objects.create(**appdata)
except Exception as e:
print(e)
delete_apps_storage(app_file_name.split(".")[0],get_release_type(app_file_name,appinfo))
delete_apps_storage(app_file_name.split(".")[0], get_release_type(app_file_name, appinfo))
return
AppReleaseInfo.objects.filter(app_id=appmobj).update(**{"is_master":False})
release_data={
"app_id":appmobj,
"icon_url":"/".join([MEDIA_URL.strip("/"),"icons",app_img]),
"release_id":app_file_name.split(".")[0],
"build_version":appinfo.get("version"),
"app_version":appinfo.get("versioncode"),
"release_type":get_release_type(app_file_name,appinfo),
"minimum_os_version":appinfo.get("miniOSversion", None),
"binary_size":os.path.getsize(app_full_path),
"is_master":True
else:
try:
appmobj.short = short
appmobj.name = appinfo["labelname"]
appmobj.save()
except Exception as e:
print(e)
# appmobj.short = short
appmobj.name = appinfo["labelname"]
appmobj.save()
AppReleaseInfo.objects.filter(app_id=appmobj).update(**{"is_master": False})
release_data = {
"app_id": appmobj,
"icon_url": "/".join([MEDIA_URL.strip("/"), "icons", app_img]),
"release_id": app_file_name.split(".")[0],
"build_version": appinfo.get("version"),
"app_version": appinfo.get("versioncode"),
"release_type": get_release_type(app_file_name, appinfo),
"minimum_os_version": appinfo.get("miniOSversion", None),
"binary_size": size,
"is_master": True,
"changelog":appinfo.get("changelog",'')
}
AppReleaseInfo.objects.create(**release_data)

@ -0,0 +1,49 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 3月
# author: liuyu
# date: 2020/3/18
from qiniu import Auth
from qiniu import BucketManager
class QiNiu(object):
def __init__(self):
access_key="mTqfvkLVSTVb2_1ERjDlFS_WAHLSkpDxYr4e4fiJ"
secret_key="0G9fXfgmi8h1-bmEsABYkE6apf8IuwKpj3hYLynv"
self.bucket_name = 'fir-storage'
self.download_domain = 'fly-cdn.dvcloud.xin'
self.qiniu_obj = Auth(access_key,secret_key)
def get_qiniu_upload_token(self,name):
# 生成上传 Token,可以指定过期时间等
# 上传策略示例
# https://developer.qiniu.com/kodo/manual/1206/put-policy
policy = {
# 'callbackUrl':'https://requestb.in/1c7q2d31',
# 'callbackBody':'filename=$(fname)&filesize=$(fsize)'
# 'persistentOps':'imageView2/1/w/200/h/200'
}
# 3600为token过期时间,秒为单位。3600等于一小时
token = self.qiniu_obj.upload_token(self.bucket_name, name, 3600, policy)
# print(token)
return token
def get_qiniu_download_token(self,name):
#有两种方式构造base_url的形式
base_url = 'http://%s/%s' % (self.download_domain, name)
#或者直接输入url的方式下载
#可以设置token过期时间
private_url = self.qiniu_obj.private_download_url(base_url, expires=3600)
print(private_url)
return private_url
def del_qiniu_file(self,name):
# 初始化BucketManager
bucket = BucketManager(self.qiniu_obj)
# 你要测试的空间, 并且这个key在你空间中存在
# 删除bucket_name 中的文件 key
ret, info = bucket.delete(self.bucket_name, name)
print(info)
return ret

@ -1,6 +1,8 @@
from rest_framework import serializers
from api import models
from api.utils.app.analyze import bytes2human,make_download_token
from api.utils.qiniu.tools import QiNiu
import os
# class CourseCategorySerializer(serializers.ModelSerializer):
@ -228,6 +230,8 @@ class AppsSerializer(serializers.ModelSerializer):
if master_release_obj:
icon_url = "/".join([obj.user_id.domain_name, master_release_obj.icon_url])
qiniu = QiNiu()
icon_url = qiniu.get_qiniu_download_token( os.path.basename(master_release_obj.icon_url))
datainfo = {
"app_version": master_release_obj.app_version,
"icon_url": icon_url,
@ -244,6 +248,11 @@ class AppsSerializer(serializers.ModelSerializer):
if self.context.get("download_token", None) and self.context.get("download_token") != "download_token":
download_url = "/".join([obj.user_id.domain_name, "download", master_release_obj.release_id])
download_url = download_url + "?token=" + self.context.get("download_token")
if obj.type == 0:
apptype='.apk'
else:
apptype = '.ipa'
download_url = qiniu.get_qiniu_download_token(master_release_obj.release_id+apptype)
datainfo["download_url"] = download_url
return datainfo
@ -273,12 +282,24 @@ class AppReleaseSerializer(serializers.ModelSerializer):
return bytes2human(obj.binary_size)
def get_download_url(self,obj):
download_url = "/".join([obj.app_id.user_id.domain_name, "download", obj.release_id])
download_url = download_url + "?token=" + make_download_token(obj.release_id,300)
if obj.release_type == 0:
apptype = '.apk'
else:
apptype = '.ipa'
qiniu = QiNiu()
download_url = qiniu.get_qiniu_download_token(obj.release_id + apptype)
return download_url
def get_icon_url(self, obj):
return "/".join([obj.app_id.user_id.domain_name, obj.icon_url])
icon_url="/".join([obj.app_id.user_id.domain_name, obj.icon_url])
qiniu = QiNiu()
icon_url = qiniu.get_qiniu_download_token(os.path.basename(obj.icon_url))
return icon_url
def get_master_color(self, obj):
if obj.is_master:

@ -25,6 +25,8 @@ class UploadView(APIView):
# 获取多个file
files = request.FILES.getlist('file', None)
if not files:
res.msg="文件不存在"
for file_obj in files:
# 将文件缓存到本地后上传
try:

@ -0,0 +1,78 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 3月
# author: liuyu
# date: 2020/3/6
from rest_framework.views import APIView
from api.utils.response import BaseResponse
from api.utils.auth import ExpiringTokenAuthentication
from api.utils.randomstrings import make_from_user_uuid
from rest_framework.response import Response
from fir_ser import settings
from api.utils.app.analyze import AnalyzeUtil, get_random_short,SaveAppInfos
import os
from api.utils.qiniu.tools import QiNiu
from api.models import Apps
from api.utils.randomstrings import make_app_uuid
class QiNiuUploadView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ]
def post(self, request):
res = BaseResponse()
# 1.接受 bundelid ,返回随机应用名称和短连接
bundleid = request.data.get("bundleid", None)
app_type = request.data.get("type", None)
if bundleid and app_type:
ap = 'apk'
if app_type == 'iOS':
ap='ipa'
app_uuid = make_app_uuid(request.user, bundleid + ap)
release_id = make_from_user_uuid(request.user)
png_id = make_from_user_uuid(request.user)
app_obj = Apps.objects.filter(app_id=app_uuid).first()
print(app_obj)
if app_obj:
short = app_obj.short
else:
short = get_random_short()
if app_type == 'iOS':
upload_key = release_id+'.ipa'
else:
upload_key = release_id+'.apk'
png_key = png_id+'.png'
qiniu = QiNiu()
res.data = {"app_uuid": app_uuid, "short": short,
"domain_name": request.user.domain_name,
"upload_token":qiniu.get_qiniu_upload_token(upload_key),
"upload_key":upload_key,
"png_token":qiniu.get_qiniu_upload_token(png_key),
"png_key":png_key}
else:
res.code = 1003
return Response(res.dict)
def put(self,request):
res = BaseResponse()
print(request.data)
data=request.data
appinfo={
"labelname":data.get("appname"),
"version":data.get("buildversion"),
"versioncode":data.get("version"),
"release_type":data.get("release_type"),
"miniOSversion":data.get("miniosversion"),
"changelog":data.get("changelog",'')
}
try:
SaveAppInfos(data.get("upload_key"), request.user, appinfo,
data.get("bundleid"), data.get("png_key"), data.get("short"), data.get('filesize'))
except Exception as e:
print(e)
res.code = 10003
return Response(res.dict)
Loading…
Cancel
Save