新增用户存储设置

super_signature
nineven 5 years ago
parent 1027110786
commit cb32a2226e
  1. 12
      fir_client/src/components/FirApps.vue
  2. 397
      fir_client/src/components/FirUserProfileStorage.vue
  3. 7
      fir_client/src/restful/index.js
  4. 28
      fir_ser/api/migrations/0024_auto_20200413_1557.py
  5. 18
      fir_ser/api/migrations/0025_appstorage_updated_time.py
  6. 18
      fir_ser/api/migrations/0026_auto_20200414_1505.py
  7. 7
      fir_ser/api/models.py
  8. 22
      fir_ser/api/utils/serializer.py
  9. 95
      fir_ser/api/views/storage.py

@ -755,11 +755,15 @@
return ftype return ftype
}, },
make_icon_url(icon_url) { make_icon_url(icon_url) {
if (!icon_url.startsWith("http")) { if(icon_url){
return location.origin + icon_url if (!icon_url.startsWith("http")) {
} else { return location.origin + icon_url
return icon_url } else {
return icon_url
}
} }
return icon_url
}, },
get_upload_text(is_new) { get_upload_text(is_new) {
if (is_new) { if (is_new) {

@ -1,19 +1,196 @@
<template> <template>
<div> <div style="text-align:center">
<el-select v-model="use_storage_id" clearable filterable placeholder="请选择" @change="select_storage"> <el-dialog :title="title" :visible.sync="dialogstorageVisible" :destroy-on-close="true" :close-on-click-modal="false">
<el-option-group
v-for="storage_group in fstorage_lists" <el-form v-if="editstorageinfo.id !==-1" ref="storageinfoform" :model="editstorageinfo"
:key="storage_group.group_name" label-width="80px" style="margin:0 auto;">
:label="storage_group.group_name"> <el-form-item label-width="100px" label="存储类型">
<el-option <el-select :disabled='disabled' v-model="editstorageinfo.storage_type" placeholder="存储类型"
v-for="storage in storage_group.storage" style="margin-left: -100px">
:key="storage.id" <el-option v-for="st in storage_list" :key="st.id" :label="st.name" :value="st.id"></el-option>
:label="storage.name" </el-select>
:value="storage.id"> </el-form-item>
</el-option>
</el-option-group> <div v-if="editstorageinfo.storage_type">
</el-select> <el-form-item label-width="110px" label="存储名称">
<el-input :disabled='disabled' v-model="editstorageinfo.name"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="key">
<el-input :disabled='disabled' v-model="editstorageinfo.access_key"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="secret">
<el-input :disabled='disabled' v-model="editstorageinfo.secret_key"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="bucket_name">
<el-input :disabled='disabled' v-model="editstorageinfo.bucket_name"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="下载域名">
<el-input :disabled='disabled' v-model="editstorageinfo.domain_name"></el-input>
</el-form-item>
<div v-if="editstorageinfo.storage_type === 2">
<el-form-item label-width="110px" label="sts_role_arn">
<el-input :disabled='disabled'
v-model="editstorageinfo.additionalparameter.sts_role_arn"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="endpoint">
<el-input :disabled='disabled' v-model="editstorageinfo.additionalparameter.endpoint"></el-input>
</el-form-item>
</div>
<el-form-item label-width="110px" label="备注">
<el-input :disabled='disabled' v-model="editstorageinfo.description"></el-input>
</el-form-item>
<el-button v-if="!disabled" @click="updateorcreate">保存</el-button>
<el-button v-if="!disabled" @click="dialogstorageVisible=false">取消</el-button>
</div>
</el-form>
</el-dialog>
<el-tabs v-model="activeName" @tab-click="handleClick" tab-position="right">
<el-tab-pane label="存储选择" name="chostorage">
<el-select v-model="use_storage_id" filterable :placeholder="selectlabel" @change="select_storage">
<el-option-group
v-for="storage_group in fstorage_lists"
:key="storage_group.group_name"
:label="storage_group.group_name">
<el-option
v-for="storage in storage_group.storages"
:key="storage.id"
:label="storage.name"
:value="storage.id">
</el-option>
</el-option-group>
</el-select>
<el-button style="margin-left: 10px" round type="info" icon="el-icon-thumb" @click="change_storage_info">保存</el-button>
<!-- <el-button-group style="margin-left: 10px">-->
<!-- <el-button round type="info" icon="el-icon-edit" @click="change_storage_info"></el-button>-->
<!-- <el-button round type="info" icon="el-icon-plus" @click="add_storage_info"></el-button>-->
<!-- <el-button round type="info" icon="el-icon-delete" @click="del_storage_info"></el-button>-->
<!-- </el-button-group>-->
<el-divider></el-divider>
<el-form v-if="storageinfo.id && storageinfo.id !==-1" ref="storageinfoform" :model="storageinfo"
label-width="80px" style="width: 39%;margin:0 auto;">
<el-form-item label-width="110px" label="存储类型">
<el-select :disabled='Sdisabled' v-model="storageinfo.storage_type" placeholder="存储类型"
style="margin-left: -60px">
<el-option v-for="st in storage_list" :key="st.id" :label="st.name" :value="st.id"></el-option>
</el-select>
</el-form-item>
<div v-if="storageinfo.storage_type">
<el-form-item label-width="110px" label="存储名称">
<el-input :disabled='Sdisabled' v-model="storageinfo.name"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="key">
<el-input :disabled='Sdisabled' v-model="storageinfo.access_key"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="secret">
<el-input :disabled='Sdisabled' v-model="storageinfo.secret_key"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="bucket_name">
<el-input :disabled='Sdisabled' v-model="storageinfo.bucket_name"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="下载域名">
<el-input :disabled='Sdisabled' v-model="storageinfo.domain_name"></el-input>
</el-form-item>
<div v-if="storageinfo.storage_type === 2">
<el-form-item label-width="110px" label="sts_role_arn">
<el-input :disabled='Sdisabled'
v-model="storageinfo.additionalparameter.sts_role_arn"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="endpoint">
<el-input :disabled='Sdisabled' v-model="storageinfo.additionalparameter.endpoint"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="备注">
<el-input :disabled='Sdisabled' v-model="storageinfo.description"></el-input>
</el-form-item>
</div>
</div>
</el-form>
</el-tab-pane>
<el-tab-pane label="存储管理" name="setstorage">
<el-table
:data="storage_info_lists"
border
stripe
style="width: 100%">
<el-table-column
fixed
prop="name"
label="存储名称"
width="180">
</el-table-column>
<el-table-column
prop="storage_type_display"
label="存储类型"
width="100">
</el-table-column>
<el-table-column
prop="domain_name"
label="下载域名"
width="160">
</el-table-column>
<el-table-column
prop="bucket_name"
label="bucket_name"
width="120">
</el-table-column>
<el-table-column
:formatter="formatter"
prop="updated_time"
label="修改时间"
width="170">
</el-table-column>
<el-table-column
prop="description"
label="备注"
width="160">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="120">
<template slot-scope="scope" >
<div v-if="scope.row.id === org_storage_id">
<el-button v-if="scope.row.id === org_storage_id" @click="showstorage(scope.row)" type="success" size="small">使用中</el-button>
</div>
<div v-else>
<el-button @click="showstorage(scope.row)" type="text" size="small">查看</el-button>
<el-button @click="editstorage(scope.row)" type="text" size="small">编辑</el-button>
<el-button @click="del_storage_info(scope.row)" type="text" size="small">删除</el-button>
</div>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="新增存储" name="addstorage" >
</el-tab-pane>
</el-tabs>
</div> </div>
</template> </template>
@ -25,39 +202,189 @@
name: "FirUserProfileStorage", name: "FirUserProfileStorage",
data() { data() {
return { return {
fstorage_lists:[[]], fstorage_lists: [],
use_storage_id:null Sdisabled:true,
use_storage_id: 0,
org_storage_id:0,
title:'',
dialogstorageVisible:false,
editstorageinfo:{'additionalparameter':{}},
selectlabel: "",
storageinfo: {'additionalparameter': {}},
storage_list: [],
disabled: true,
isaddflag: false,
activeName: 'chostorage',
storage_info_lists: [],
} }
}, methods: { }, methods: {
select_storage(a){ deepCopy(source) {
// eslint-disable-next-line no-console let result={};
console.log(a) for (let key in source) {
result[key] = typeof source[key]==='object'? this.deepCopy(source[key]): source[key];
}
return result;
},
showstorage(editstorageinfo){
this.title='查看存储信息';
this.disabled=true;
this.dialogstorageVisible=true;
this.editstorageinfo=this.deepCopy(editstorageinfo);
},
editstorage(editstorageinfo){
this.title='存储编辑';
this.disabled=false;
this.dialogstorageVisible=true;
this.editstorageinfo=this.deepCopy(editstorageinfo);
this.isaddflag=false;
},add_storage_click(){
this.title='新增存储';
this.disabled=false;
this.dialogstorageVisible=true;
this.editstorageinfo={'additionalparameter':{}};
this.isaddflag=true;
},
// eslint-disable-next-line no-unused-vars
handleClick(tab, event) {
if(tab.name === 'addstorage'){
this.add_storage_click()
}
}, },
format_storage(storage_lists){ updateorcreate() {
let methods = "PUT";
if (this.isaddflag) {
methods = "POST"
}
getStorageinfo(data => {
if (data.code === 1000) {
this.$message.success('操作成功');
this.dialogstorageVisible=false;
if (this.isaddflag) {
this.activeName='setstorage';
}
this.getstorageinfoFun();
}
}, {"methods": methods, 'data': this.editstorageinfo});
this.getstorageinfoFun();
}, del_storage_info(sinfo) {
for(let key in storage_lists){ this.$confirm('此操作可能会导致程序异常, 是否继续?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
getStorageinfo(data => {
if (data.code === 1000) {
this.$message.success('删除成功');
this.getstorageinfoFun();
}
}, {"methods": 'DELETE', 'data': {'id': sinfo.id, 'tid': sinfo.storage_type}})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
change_storage_info() {
this.$confirm('此操作将导致应用,图片,显示下载异常, 是否继续?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
getStorageinfo(data => {
if (data.code === 1000) {
this.$message.success('修改成功');
this.getstorageinfoFun();
}
}, {"methods": 'PUT', 'data': {'use_storage_id':this.use_storage_id}});
this.getstorageinfoFun();
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
});
},
select_storage(a) {
this.disabled = true;
this.getstorageinfobyid(a);
},
getstorageinfobyid(id) {
for (let i = 0; i < this.fstorage_lists.length; i++) {
let storages = this.fstorage_lists[i].storages;
for (let j = 0; j < storages.length; j++) {
if (id === storages[j].id) {
this.storageinfo = storages[j];
break
}
}
}
},
format_storage(storage_data_lists) {
let storage_f = [];
let storage_lists = {};
for (let key in storage_data_lists) {
let storage = storage_data_lists[key];
let storage_type = storage.storage_type;
if (!storage_lists[storage_type]) {
storage_lists[storage_type] = [];
}
storage_lists[storage_type].unshift(storage);
}
for (let key in storage_lists) {
let storage = storage_lists[key]; let storage = storage_lists[key];
if(storage[0].storage_type_display){ if (storage[0].storage_type_display) {
this.fstorage_lists.unshift({'group_name':storage[0].storage_type_display,'storage':storage}) storage_f.unshift({'group_name': storage[0].storage_type_display, 'storages': storage})
} }
if (!this.selectlabel) {
for (let i = 0; i < storage.length; i++) {
if (this.use_storage_id === storage[i].id) {
this.selectlabel = storage[i].name;
break
}
}
}
}
storage_f.unshift({'group_name': '默认存储', 'storages': [{'id': -1, 'name': '默认存储'}]});
if (!this.selectlabel) {
this.selectlabel = "默认存储"
} }
this.fstorage_lists = storage_f.slice();
this.getstorageinfobyid(this.use_storage_id)
}, },
getstorageinfoFun() { getstorageinfoFun() {
// eslint-disable-next-line no-console getStorageinfo(data => {
getStorageinfo(data=>{ if (data.code === 1000) {
if(data.code === 1000){ this.org_storage_id=this.use_storage_id = data.storage;
this.format_storage(data.data); this.storage_list = data.storage_list;
this.use_storage_id = data.data.storage; this.storage_info_lists = data.data;
}else { this.format_storage(data.data);
this.$message.error('存储获取失败,'+data); this.disabled=true;
} this.isaddflag=false;
},{"methods":false}); } else {
} this.$message.error('存储获取失败,' + data);
}
}, {"methods": 'GET'});
},
// eslint-disable-next-line no-unused-vars
formatter(row, column) {
let stime = row.updated_time;
if (stime) {
stime = stime.split(".")[0].split("T");
return stime[0] + " " + stime[1]
} else
return '';
},
}, mounted() { }, mounted() {
this.$store.dispatch('douserInfoIndex', 2); this.$store.dispatch('douserInfoIndex', 2);
this.getstorageinfoFun(); this.getstorageinfoFun();
} }, filters: {}
} }
</script> </script>

@ -1,6 +1,6 @@
import Axios from 'axios' import Axios from 'axios'
import VueCookies from 'vue-cookies' import VueCookies from 'vue-cookies'
import router from "../router";
const https = require('https'); const https = require('https');
const Base64 = require('js-base64').Base64; const Base64 = require('js-base64').Base64;
Axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; Axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
@ -45,7 +45,7 @@ function getData(methods = true, url, params = {}, callBack, load, isCode = fals
if (methods === "DELETE") { if (methods === "DELETE") {
Axios Axios
.delete(url, params) .delete(url, {params:params})
.then(function (response) { .then(function (response) {
if (isCode) { if (isCode) {
callBack(response.data); callBack(response.data);
@ -150,6 +150,9 @@ function getData(methods = true, url, params = {}, callBack, load, isCode = fals
.catch(function (error) { .catch(function (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error, error.response); console.log(error, error.response);
if(error.response && error.response.status === 403){
router.push({name: 'FirLogin'});
}
callBack(null); callBack(null);
}); });
} }

@ -0,0 +1,28 @@
# Generated by Django 3.0.3 on 2020-04-13 15:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0023_appreleaseinfo_udid'),
]
operations = [
migrations.AlterField(
model_name='appreleaseinfo',
name='udid',
field=models.TextField(blank=True, default='', null=True, verbose_name='ios内测版 udid'),
),
migrations.AlterField(
model_name='appstorage',
name='additionalparameters',
field=models.TextField(blank=True, default=None, help_text='阿里云:{"sts_role_arn":"arn信息","endpoint":""} ', null=True, verbose_name='额外参数'),
),
migrations.AlterField(
model_name='appstorage',
name='is_https',
field=models.BooleanField(default=True, verbose_name='是否支持https'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-04-14 10:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0024_auto_20200413_1557'),
]
operations = [
migrations.AddField(
model_name='appstorage',
name='updated_time',
field=models.DateTimeField(auto_now=True, verbose_name='更新时间'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 3.0.3 on 2020-04-14 15:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0025_appstorage_updated_time'),
]
operations = [
migrations.AlterField(
model_name='appstorage',
name='description',
field=models.TextField(blank=True, default='', null=True, verbose_name='备注'),
),
]

@ -152,10 +152,11 @@ class AppStorage(models.Model):
bucket_name=models.CharField(max_length=128,blank=True, null=True, verbose_name="存储空间bucket_name") bucket_name=models.CharField(max_length=128,blank=True, null=True, verbose_name="存储空间bucket_name")
domain_name=models.CharField(max_length=128,blank=True, null=True, verbose_name="下载域名",help_text='fly-storage.dvcloud.xin,可以自定义端口') domain_name=models.CharField(max_length=128,blank=True, null=True, verbose_name="下载域名",help_text='fly-storage.dvcloud.xin,可以自定义端口')
is_https = models.BooleanField(default=True, verbose_name="是否支持https") is_https = models.BooleanField(default=True, verbose_name="是否支持https")
additionalparameters = models.TextField(max_length=256,blank=True, null=True,verbose_name="额外参数", additionalparameters = models.TextField(blank=True, null=True,verbose_name="额外参数",
help_text='阿里云:{"sts_role_arn":"arn信息","endpoint":""} ') help_text='阿里云:{"sts_role_arn":"arn信息","endpoint":""} ', default=None)
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
description = models.TextField('备注', blank=True, null=True, default=None, ) updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")
description = models.TextField('备注', blank=True, null=True, default='')
class Meta: class Meta:
verbose_name = '存储配置' verbose_name = '存储配置'

@ -3,7 +3,7 @@ from api import models
from api.utils.app.apputils import bytes2human from api.utils.app.apputils import bytes2human
from api.utils.TokenManager import DownloadToken from api.utils.TokenManager import DownloadToken
from api.utils.storage.storage import Storage from api.utils.storage.storage import Storage
import os import os,json
token_obj = DownloadToken() token_obj = DownloadToken()
@ -192,7 +192,23 @@ class AppReleaseSerializer(serializers.ModelSerializer):
class StorageSerializer(serializers.ModelSerializer): class StorageSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.AppStorage model = models.AppStorage
# depth = 1
exclude = ["user_id"] exclude = ["user_id"]
storage_type_display= serializers.CharField(source="get_storage_type_display") storage_type_display= serializers.CharField(source="get_storage_type_display",read_only=True)
additionalparameters=serializers.CharField(write_only=True)
additionalparameter=serializers.SerializerMethodField(read_only=True)
def get_additionalparameter(self,obj):
infos={}
try:
infos = json.loads(obj.additionalparameters)
except Exception as e:
print(e)
return infos
def create(self, validated_data):
if self.context.get("user_obj", None) and self.context.get("user_obj") != "undefined":
user_obj = self.context.get("user_obj", None)
storage_obj = models.AppStorage.objects.create(**validated_data,user_id=user_obj,)
return storage_obj
return None

@ -8,40 +8,111 @@ from rest_framework.views import APIView
from api.utils.response import BaseResponse from api.utils.response import BaseResponse
from api.utils.auth import ExpiringTokenAuthentication from api.utils.auth import ExpiringTokenAuthentication
from rest_framework.response import Response from rest_framework.response import Response
import json
from django.db.models import Sum from django.db.models import Sum
import os import os
from fir_ser import settings from fir_ser import settings
from api.utils.app.randomstrings import make_from_user_uuid from api.utils.app.randomstrings import make_from_user_uuid
from api.utils.storage.storage import Storage from api.utils.storage.storage import Storage
from api.utils.storage.caches import del_cache_response_by_short,get_app_today_download_times from api.utils.storage.caches import del_cache_response_by_short, get_app_today_download_times
from api.models import Apps, AppReleaseInfo,AppStorage from api.models import AppStorage,UserInfo
from api.utils.serializer import StorageSerializer from api.utils.serializer import StorageSerializer
class StorageView(APIView): class StorageView(APIView):
authentication_classes = [ExpiringTokenAuthentication, ] authentication_classes = [ExpiringTokenAuthentication, ]
def get(self, request): def get(self, request):
res = BaseResponse() res = BaseResponse()
# [1,2] 表示七牛存储和阿里云存储 # [1,2] 表示七牛存储和阿里云存储
storage_obj = AppStorage.objects.filter(user_id=request.user,storage_type__in=[1,2]) storage_obj = AppStorage.objects.filter(user_id=request.user, storage_type__in=[1, 2])
if storage_obj: if storage_obj:
storage_serializer = StorageSerializer(storage_obj,many=True) storage_serializer = StorageSerializer(storage_obj, many=True)
storage_data_lists = storage_serializer.data storage_data_lists = storage_serializer.data
storage_lists={} storage_lists = {}
for storage_data in storage_data_lists: for storage_data in storage_data_lists:
storage_type = storage_data.get("storage_type") storage_type = storage_data.get("storage_type")
if not storage_lists.get(storage_type): if not storage_lists.get(storage_type):
storage_lists[storage_type]=[] storage_lists[storage_type] = []
storage_lists[storage_type].append(storage_data) storage_lists[storage_type].append(storage_data)
res.data=storage_lists res.data = storage_data_lists
use_storage_obj = request.user.storage use_storage_obj = request.user.storage
if use_storage_obj: if use_storage_obj:
res.storage=use_storage_obj.id res.storage = use_storage_obj.id
else:
res.storage = -1 # 默认存储
res.storage_list = []
storage_org_list = list(AppStorage.storage_choices)
for storage_t in storage_org_list:
if storage_t[0] in [0, 3]: continue
res.storage_list.append({'id': storage_t[0], 'name': storage_t[1]})
return Response(res.dict)
def post(self, request):
res = BaseResponse()
data = request.data
try:
data['additionalparameters'] = json.dumps(data.get('additionalparameter', ''))
except Exception as e:
print(e)
serializer = StorageSerializer(data=data, context={'user_obj': request.user})
if serializer.is_valid():
serializer.save()
res.msg = serializer.validated_data
else: else:
res.storage = None res.msg = serializer.errors
res.code = 1005
return Response(res.dict)
def put(self, request):
res = BaseResponse()
data = request.data
use_storage_id=data.get("use_storage_id",None)
if use_storage_id:
try:
if use_storage_id == -1:
UserInfo.objects.filter(pk=request.user.pk).update(storage=None)
else:
UserInfo.objects.filter(pk=request.user.pk).update(storage_id=use_storage_id)
except Exception as e:
print(e)
res.code=1006
res.msg='修改失败'
return Response(res.dict)
try:
data['additionalparameters'] = json.dumps(data.get('additionalparameter', ''))
except Exception as e:
print(e)
storage_id = data.get("id", None)
if storage_id:
storage_obj = AppStorage.objects.filter(id=storage_id, user_id=request.user).first()
serializer = StorageSerializer(instance=storage_obj, data=data, context={'user_obj': request.user},
partial=True)
if serializer.is_valid():
serializer.save()
res.msg = serializer.validated_data
else:
res.msg = serializer.errors
res.code = 1005
else:
res.msg = '该存储不存在'
res.code = 1007
return Response(res.dict)
def delete(self, request):
res = BaseResponse()
storage_id = request.query_params.get("id", None)
if storage_id:
AppStorage.objects.filter(user_id=request.user, id=storage_id).delete()
else:
res.code = 1004
res.msg = '该存储不存在'
return Response(res.dict) return Response(res.dict)

Loading…
Cancel
Save