优化代码,增加添加oss存储 校验功能

super_signature
nineven 5 years ago
parent b32a12a597
commit c46bbc6f75
  1. 6
      fir_client/babel.config.js
  2. 18
      fir_client/public/index.html
  3. 14
      fir_client/src/components/FirAppInfossecurity.vue
  4. 30
      fir_client/src/components/FirLogin.vue
  5. 3
      fir_client/src/components/FirUserProfileStorage.vue
  6. 10
      fir_client/src/restful/index.js
  7. 1
      fir_ser/api/migrations/0007_auto_20200520_1153.py
  8. 2
      fir_ser/api/utils/app/apputils.py
  9. 2
      fir_ser/api/utils/app/supersignutils.py
  10. 2
      fir_ser/api/utils/serializer.py
  11. 10
      fir_ser/api/utils/storage/aliyunApi.py
  12. 5
      fir_ser/api/utils/storage/localApi.py
  13. 11
      fir_ser/api/utils/storage/qiniuApi.py
  14. 36
      fir_ser/api/utils/storage/storage.py
  15. 9
      fir_ser/api/utils/utils.py
  16. 2
      fir_ser/api/views/download.py
  17. 36
      fir_ser/api/views/storage.py
  18. 2
      fir_ser/api/views/supersign.py

@ -1,5 +1,5 @@
module.exports = { module.exports = {
presets: [ presets: [
'@vue/cli-plugin-babel/preset' '@vue/cli-plugin-babel/preset'
] ]
}; };

@ -1,17 +1,17 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>fly分发平台</title> <title>fly分发平台</title>
</head> </head>
<body> <body>
<noscript> <noscript>
<strong>We're sorry but fly doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> <strong>We're sorry but fly doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript> </noscript>
<div id="app"></div> <div id="app"></div>
<!-- built files will be auto injected --> <!-- built files will be auto injected -->
</body> </body>
</html> </html>

@ -22,7 +22,7 @@
</el-switch> </el-switch>
</el-tooltip> </el-tooltip>
<el-link :underline="false" style="margin-left: 20px" >设置密码之后用户需要输入密码才可以下载该应用</el-link> <el-link :underline="false" style="margin-left: 20px">设置密码之后用户需要输入密码才可以下载该应用</el-link>
</el-form-item> </el-form-item>
@ -38,7 +38,7 @@
inactive-value="off"> inactive-value="off">
</el-switch> </el-switch>
</el-tooltip> </el-tooltip>
<el-link :underline="false" style="margin-left: 20px" >默认开启关闭之后用户无法通过短连接访问下载该应用</el-link> <el-link :underline="false" style="margin-left: 20px">默认开启关闭之后用户无法通过短连接访问下载该应用</el-link>
</el-form-item> </el-form-item>
@ -57,11 +57,11 @@
<el-button @click="clean_app" v-if="currentapp.issupersign === 0 && currentapp.count !== 0" <el-button @click="clean_app" v-if="currentapp.issupersign === 0 && currentapp.count !== 0"
style="margin-left: 20px" size="small" type="info" plain>清理开发者账户脏数据 style="margin-left: 20px" size="small" type="info" plain>清理开发者账户脏数据
</el-button> </el-button>
<el-link v-else :underline="false" style="margin-left: 20px" >超级签名iOS专用需要配置好苹果开发者账户方可开启</el-link> <el-link v-else :underline="false" style="margin-left: 20px">超级签名iOS专用需要配置好苹果开发者账户方可开启</el-link>
</el-form-item> </el-form-item>
<el-form-item label-width="200px" label="微信内访问简易模式"> <el-form-item label-width="200px" label="微信内访问简易模式">
<el-tooltip :content="wxeasytypetip.msg" placement="top"> <el-tooltip :content="wxeasytypetip.msg" placement="top">
<el-switch <el-switch
@ -73,9 +73,9 @@
inactive-value="off"> inactive-value="off">
</el-switch> </el-switch>
</el-tooltip> </el-tooltip>
<el-link :underline="false" style="margin-left: 20px" >默认开启可以最大限度避免微信内举报封停</el-link> <el-link :underline="false" style="margin-left: 20px">默认开启可以最大限度避免微信内举报封停</el-link>
</el-form-item> </el-form-item>
<el-form-item label-width="200px" label="微信内访问跳转第三方平台"> <el-form-item label-width="200px" label="微信内访问跳转第三方平台">
<el-tooltip :content="wxredirecttip.msg" placement="top"> <el-tooltip :content="wxredirecttip.msg" placement="top">
<el-switch <el-switch
@ -87,7 +87,7 @@
inactive-value="off"> inactive-value="off">
</el-switch> </el-switch>
</el-tooltip> </el-tooltip>
<el-link :underline="false" style="margin-left: 20px" >默认开启如果配置第三方平台在微信内访问直接跳转</el-link> <el-link :underline="false" style="margin-left: 20px">默认开启如果配置第三方平台在微信内访问直接跳转</el-link>
</el-form-item> </el-form-item>

@ -67,9 +67,9 @@
form: { form: {
email: '', email: '',
password: '', password: '',
authcode:'' authcode: ''
}, },
cptch:{"cptch_image": '', "cptch_key": '',"length":8}, cptch: {"cptch_image": '', "cptch_key": '', "length": 8},
} }
}, },
methods: { methods: {
@ -77,7 +77,7 @@
let email = this.form.email; let email = this.form.email;
let password = this.form.password; let password = this.form.password;
let authcode = this.form.authcode; let authcode = this.form.authcode;
if(authcode.length === this.cptch.length){ if (authcode.length === this.cptch.length) {
if (this.isEmail(email) && password.length > 6) { if (this.isEmail(email) && password.length > 6) {
loginFun(data => { loginFun(data => {
if (data.code == 1000) { if (data.code == 1000) {
@ -86,9 +86,9 @@
type: 'success' type: 'success'
}); });
this.$cookies.remove("auth_token"); this.$cookies.remove("auth_token");
this.$cookies.set("token", data['token'],3600 * 24 * 30); this.$cookies.set("token", data['token'], 3600 * 24 * 30);
this.$cookies.set("username", data.userinfo.username,3600 * 24 * 30); this.$cookies.set("username", data.userinfo.username, 3600 * 24 * 30);
this.$cookies.set("first_name", data.userinfo.first_name,3600 * 24 * 30); this.$cookies.set("first_name", data.userinfo.first_name, 3600 * 24 * 30);
this.$store.dispatch("doUserinfo", data.userinfo); this.$store.dispatch("doUserinfo", data.userinfo);
set_auth_token(); set_auth_token();
this.$router.push({name: 'FirApps'}) this.$router.push({name: 'FirApps'})
@ -100,12 +100,12 @@
this.get_auth_code(); this.get_auth_code();
} }
}, { }, {
"methods":"POST", "methods": "POST",
"data":{ "data": {
"username": email, "username": email,
"password": password, "password": password,
"authcode":authcode, "authcode": authcode,
"cptch_key":this.cptch.cptch_key "cptch_key": this.cptch.cptch_key
} }
}); });
@ -116,7 +116,7 @@
type: 'warning' type: 'warning'
}); });
} }
}else { } else {
this.$message({ this.$message({
message: '验证码有误', message: '验证码有误',
type: 'warning' type: 'warning'
@ -132,10 +132,10 @@
} }
return false; return false;
}, },
get_auth_code(){ get_auth_code() {
loginFun(data => { loginFun(data => {
if (data.code == 1000) { if (data.code == 1000) {
this.cptch=data.data; this.cptch = data.data;
} else { } else {
this.$message({ this.$message({
message: data.msg, message: data.msg,
@ -143,8 +143,8 @@
}); });
} }
}, { }, {
"methods":"GET", "methods": "GET",
"data":{} "data": {}
}); });
}, },
}, },

@ -273,9 +273,10 @@
} }
this.getstorageinfoFun(); this.getstorageinfoFun();
} else {
this.$message.error('操作失败,' + data.msg);
} }
}, {"methods": methods, 'data': this.editstorageinfo}); }, {"methods": methods, 'data': this.editstorageinfo});
this.getstorageinfoFun();
}, del_storage_info(sinfo) { }, del_storage_info(sinfo) {
this.$confirm('此操作可能会导致程序异常, 是否继续?', '警告', { this.$confirm('此操作可能会导致程序异常, 是否继续?', '警告', {

@ -30,7 +30,7 @@ export function set_auth_token() {
} else { } else {
let token = VueCookies.get('token'); let token = VueCookies.get('token');
let username = VueCookies.get('username'); let username = VueCookies.get('username');
VueCookies.set("auth_token", Base64.encode(token + ':' + username),3600 * 24 * 30); VueCookies.set("auth_token", Base64.encode(token + ':' + username), 3600 * 24 * 30);
} }
} }
// 更改加载的样式 // 更改加载的样式
@ -111,7 +111,7 @@ function getData(methods = true, url, params = {}, callBack, load, isCode = fals
}) })
.catch(function (error) { .catch(function (error) {
ErrorMsg(error); ErrorMsg(error);
callBack({"code":-1}); callBack({"code": -1});
}); });
} else if (methods === "PUT") { } else if (methods === "PUT") {
@ -126,7 +126,7 @@ function getData(methods = true, url, params = {}, callBack, load, isCode = fals
}) })
.catch(function (error) { .catch(function (error) {
ErrorMsg(error); ErrorMsg(error);
callBack({"code":-1}); callBack({"code": -1});
}); });
} else if (methods === 'POST') { } else if (methods === 'POST') {
@ -142,7 +142,7 @@ function getData(methods = true, url, params = {}, callBack, load, isCode = fals
}) })
.catch(function (error) { .catch(function (error) {
ErrorMsg(error); ErrorMsg(error);
callBack({"code":-1}); callBack({"code": -1});
}); });
} else { } else {
Axios Axios
@ -152,7 +152,7 @@ function getData(methods = true, url, params = {}, callBack, load, isCode = fals
}) })
.catch(function (error) { .catch(function (error) {
ErrorMsg(error); ErrorMsg(error);
callBack({"code":-1}); callBack({"code": -1});
}); });
} }
} }

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('api', '0006_udidsyncdeveloper'), ('api', '0006_udidsyncdeveloper'),
] ]

@ -60,7 +60,7 @@ def make_resigned(bin_url, img_url, bundle_id, app_version, name):
</dict> </dict>
</plist>""" % (bin_url, img_url, img_url, bundle_id, app_version, name) </plist>""" % (bin_url, img_url, img_url, bundle_id, app_version, name)
logger.info("make_resigned bin_url %s ,img_url %s, img_url %s, bundle_id %s, app_version %s, name %s" % ( logger.info("make_resigned bin_url %s ,img_url %s, img_url %s, bundle_id %s, app_version %s, name %s" % (
bin_url, img_url, img_url, bundle_id, app_version, name)) bin_url, img_url, img_url, bundle_id, app_version, name))
return ios_plist_tem return ios_plist_tem

@ -203,7 +203,7 @@ class IosUtils(object):
developer_obj = self.developer_obj developer_obj = self.developer_obj
developer_obj.use_number = developer_obj.use_number + 1 developer_obj.use_number = developer_obj.use_number + 1
logger.info("developer %s use_number+1 now %s udid %s app_id %s" % ( logger.info("developer %s use_number+1 now %s udid %s app_id %s" % (
developer_obj, developer_obj.use_number, self.udid_info.get('udid'), self.app_obj)) developer_obj, developer_obj.use_number, self.udid_info.get('udid'), self.app_obj))
developer_obj.save() developer_obj.save()
if not appsupersign_obj.filter(app_id=self.app_obj, user_id=self.user_obj).first(): if not appsupersign_obj.filter(app_id=self.app_obj, user_id=self.user_obj).first():

@ -88,7 +88,7 @@ class AppsShortSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.Apps model = models.Apps
fields = ["app_id", "name", "short", "has_combo", "isshow", "description", "need_password", 'master_release', fields = ["app_id", "name", "short", "has_combo", "isshow", "description", "need_password", 'master_release',
'type', 'issupersign','wxeasytype','wxredirect'] 'type', 'issupersign', 'wxeasytype', 'wxredirect']
need_password = serializers.SerializerMethodField() need_password = serializers.SerializerMethodField()

@ -124,3 +124,13 @@ class AliYunOss(object):
def rename_file(self, oldfilename, newfilename): def rename_file(self, oldfilename, newfilename):
self.bucket.copy_object(self.bucket_name, oldfilename, newfilename) self.bucket.copy_object(self.bucket_name, oldfilename, newfilename)
return self.del_file(oldfilename) return self.del_file(oldfilename)
def upload_file(self, local_file_full_path):
if os.path.isfile(local_file_full_path):
with open(local_file_full_path, 'rb') as fileobj:
# Seek方法用于指定从第1000个字节位置开始读写。上传时会从您指定的第1000个字节位置开始上传,直到文件结束。
fileobj.seek(1000, os.SEEK_SET)
# Tell方法用于返回当前位置。
# current = fileobj.tell()
self.bucket.put_object(os.path.basename(local_file_full_path), fileobj)
return True

@ -54,3 +54,8 @@ class LocalStorage(object):
except Exception as e: except Exception as e:
logger.error("rename_file file %s to %s failed Exception %s" % (oldfilename, newfilename, e)) logger.error("rename_file file %s to %s failed Exception %s" % (oldfilename, newfilename, e))
return False return False
def upload_file(self, local_file_full_path):
if os.path.isfile(local_file_full_path):
return True
return False

@ -6,8 +6,9 @@
''' '''
主要是调用七牛云存储处理七牛云存储的逻辑 主要是调用七牛云存储处理七牛云存储的逻辑
''' '''
from qiniu import Auth from qiniu import Auth, put_file, etag
from qiniu import BucketManager from qiniu import BucketManager
import os
class QiNiuOss(object): class QiNiuOss(object):
@ -54,3 +55,11 @@ class QiNiuOss(object):
bucket = BucketManager(self.qiniu_obj) bucket = BucketManager(self.qiniu_obj)
ret, info = bucket.move(self.bucket_name, oldfilename, self.bucket_name, newfilename) ret, info = bucket.move(self.bucket_name, oldfilename, self.bucket_name, newfilename)
return ret return ret
def upload_file(self, local_file_full_path):
if os.path.isfile(local_file_full_path):
filename = os.path.basename(local_file_full_path)
token = self.get_upload_token(filename)
ret, info = put_file(token, filename, local_file_full_path)
if ret['key'] == filename and ret['hash'] == etag(local_file_full_path):
return True

@ -17,9 +17,9 @@ logger = logging.getLogger(__file__)
class Storage(object): class Storage(object):
def __init__(self, user): def __init__(self, user, storage_obj=None):
try: try:
self.storage = self.get_storage(user) self.storage = self.get_storage(user, storage_obj)
except Exception as e: except Exception as e:
logger.error("get %s storage failed Exception:%s" % (user, e)) logger.error("get %s storage failed Exception:%s" % (user, e))
self.storage = None self.storage = None
@ -48,21 +48,37 @@ class Storage(object):
filename = filename + '.apk' filename = filename + '.apk'
else: else:
filename = filename + '.ipa' filename = filename + '.ipa'
return self.storage.del_file(filename) try:
return self.storage.del_file(filename)
except Exception as e:
logger.error("delete file %s failed Exception %s" % (filename, e))
def rename_file(self, oldfilename, newfilename): def rename_file(self, oldfilename, newfilename):
if self.storage: if self.storage:
return self.storage.rename_file(oldfilename, newfilename) try:
return self.storage.rename_file(oldfilename, newfilename)
except Exception as e:
logger.error("rename %s to %s failed Exception %s" % (oldfilename, newfilename, e))
def get_storage(self, user): def upload_file(self, local_file_full_path):
self.storage_obj = user.storage if self.storage:
try:
return self.storage.upload_file(local_file_full_path)
except Exception as e:
logger.error("oss upload %s failed Exception %s" % (local_file_full_path, e))
def get_storage(self, user, storage_obj):
if storage_obj:
self.storage_obj = storage_obj
else:
self.storage_obj = user.storage
if self.storage_obj: if self.storage_obj:
auth = self.get_storage_auth(self.storage_obj) auth = self.get_storage_auth(self.storage_obj)
storage_key = "_".join([CACHE_KEY_TEMPLATE.get('user_storage_key'), user.uid, storage_key = "_".join([CACHE_KEY_TEMPLATE.get('user_storage_key'), user.uid,
base64.b64encode(json.dumps(auth).encode("utf-8")).decode("utf-8")[0:64]]) base64.b64encode(json.dumps(auth).encode("utf-8")).decode("utf-8")[0:64]])
storage_type = self.storage_obj.storage_type storage_type = self.storage_obj.storage_type
new_storage_obj = cache.get(storage_key) new_storage_obj = cache.get(storage_key)
if new_storage_obj: if new_storage_obj and not storage_obj:
return new_storage_obj return new_storage_obj
else: else:
if storage_type == 1: if storage_type == 1:
@ -76,12 +92,12 @@ class Storage(object):
cache.set(storage_key, new_storage_obj, 600) cache.set(storage_key, new_storage_obj, 600)
return new_storage_obj return new_storage_obj
else: else:
return self.get_default_storage(user) return self.get_default_storage(user, storage_obj)
def get_default_storage(self, user): def get_default_storage(self, user, storage_obj):
admin_obj = UserInfo.objects.filter(is_superuser=True).order_by('pk').first() admin_obj = UserInfo.objects.filter(is_superuser=True).order_by('pk').first()
if admin_obj and admin_obj.storage: if admin_obj and admin_obj.storage:
return self.get_storage(admin_obj) return self.get_storage(admin_obj, storage_obj)
else: else:
storage_lists = THIRD_PART_CONFIG.get('storage') storage_lists = THIRD_PART_CONFIG.get('storage')
for storage in storage_lists: for storage in storage_lists:

@ -4,11 +4,12 @@
# author: liuyu # author: liuyu
# date: 2020/5/7 # date: 2020/5/7
import os import os
from fir_ser.settings import SUPER_SIGN_ROOT, SERVER_DOMAIN, CAPTCHA_LENGTH from fir_ser.settings import SUPER_SIGN_ROOT, SERVER_DOMAIN, CAPTCHA_LENGTH, MEDIA_ROOT
from api.models import APPSuperSignUsedInfo, APPToDeveloper, \ from api.models import APPSuperSignUsedInfo, APPToDeveloper, \
UDIDsyncDeveloper UDIDsyncDeveloper
from api.utils.app.randomstrings import make_app_uuid from api.utils.app.randomstrings import make_app_uuid
from api.utils.storage.localApi import LocalStorage from api.utils.storage.localApi import LocalStorage
from api.utils.storage.storage import Storage
from django.db.models import Sum from django.db.models import Sum
from captcha.models import CaptchaStore from captcha.models import CaptchaStore
from captcha.helpers import captcha_image_url from captcha.helpers import captcha_image_url
@ -104,3 +105,9 @@ def valid_captcha(cptch_key, code, username):
if cptch_key and code and code.strip(" ").lower() == challenge.get("challenge").lower(): if cptch_key and code and code.strip(" ").lower() == challenge.get("challenge").lower():
return True return True
return False return False
def upload_oss_default_head_img(user_obj, storage_obj):
head_img_full_path = os.path.join(MEDIA_ROOT, "head_img.jpeg")
storage_obj = Storage(user_obj, storage_obj)
return storage_obj.upload_file(head_img_full_path)

@ -175,7 +175,7 @@ class InstallView(APIView):
res.data = {"download_url": download_url} res.data = {"download_url": download_url}
if download_url != "" and "mobileconifg" not in download_url: if download_url != "" and "mobileconifg" not in download_url:
set_app_download_by_cache(app_id) set_app_download_by_cache(app_id)
if request.META.get('HTTP_X_FORWARDED_FOR',None): if request.META.get('HTTP_X_FORWARDED_FOR', None):
ip = request.META['HTTP_X_FORWARDED_FOR'] ip = request.META['HTTP_X_FORWARDED_FOR']
else: else:
ip = request.META['REMOTE_ADDR'] ip = request.META['REMOTE_ADDR']

@ -11,6 +11,7 @@ from rest_framework.response import Response
import json import json
from api.utils.storage.caches import del_cache_storage from api.utils.storage.caches import del_cache_storage
from api.models import AppStorage, UserInfo from api.models import AppStorage, UserInfo
from api.utils.utils import upload_oss_default_head_img
from api.utils.serializer import StorageSerializer from api.utils.serializer import StorageSerializer
import logging import logging
@ -65,9 +66,19 @@ class StorageView(APIView):
serializer = StorageSerializer(data=data, context={'user_obj': request.user}) serializer = StorageSerializer(data=data, context={'user_obj': request.user})
if serializer.is_valid(): if serializer.is_valid():
serializer.save() storage_obj = serializer.save()
res.msg = serializer.validated_data if storage_obj:
logger.info("user %s add new storage success" % (request.user)) if upload_oss_default_head_img(request.user, storage_obj):
res.msg = serializer.validated_data
logger.info("user %s add new storage success" % (request.user))
else:
logger.error("user %s add new storage failed" % (request.user))
res.msg = "文件上传校验失败,请检查参数是否正确"
res.code = 1005
else:
logger.info("user %s add new storage failed" % (request.user))
res.msg = serializer.errors
res.code = 1005
else: else:
logger.info("user %s add new storage failed" % (request.user)) logger.info("user %s add new storage failed" % (request.user))
res.msg = serializer.errors res.msg = serializer.errors
@ -101,12 +112,27 @@ class StorageView(APIView):
storage_id = data.get("id", None) storage_id = data.get("id", None)
if storage_id: if storage_id:
if storage_id == request.user.storage.id:
res.msg = '存储正在使用中,无法修改'
res.code = 1007
return Response(res.dict)
storage_obj = AppStorage.objects.filter(id=storage_id, user_id=request.user).first() 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}, serializer = StorageSerializer(instance=storage_obj, data=data, context={'user_obj': request.user},
partial=True) partial=True)
if serializer.is_valid(): if serializer.is_valid():
serializer.save() storage_obj = serializer.save()
res.msg = serializer.validated_data if storage_obj:
if upload_oss_default_head_img(request.user, storage_obj):
res.msg = serializer.validated_data
logger.info("user %s add new storage success" % (request.user))
else:
logger.error("user %s add new storage failed" % (request.user))
res.msg = "文件上传校验失败,请检查参数是否正确"
res.code = 1005
else:
logger.info("user %s add new storage failed" % (request.user))
res.msg = serializer.errors
res.code = 1005
else: else:
res.msg = serializer.errors res.msg = serializer.errors
res.code = 1005 res.code = 1005

@ -110,7 +110,7 @@ class DeveloperView(APIView):
else: else:
logger.info("user %s iosdeveloper %s update input data %s" % (request.user, developer_obj, data)) logger.info("user %s iosdeveloper %s update input data %s" % (request.user, developer_obj, data))
logger.info("user %s iosdeveloper %s update old data %s" % ( logger.info("user %s iosdeveloper %s update old data %s" % (
request.user, developer_obj, developer_obj.__dict__)) request.user, developer_obj, developer_obj.__dict__))
try: try:
usable_number = int(data.get("usable_number", developer_obj.usable_number)) usable_number = int(data.get("usable_number", developer_obj.usable_number))
if usable_number >= 0 and usable_number <= 100: if usable_number >= 0 and usable_number <= 100:

Loading…
Cancel
Save