You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
391 lines
18 KiB
391 lines
18 KiB
#!/usr/bin/env python
|
|
# -*- coding:utf-8 -*-
|
|
# project: 3月
|
|
# author: liuyu
|
|
# date: 2020/3/6
|
|
import json
|
|
import logging
|
|
import os
|
|
|
|
from rest_framework.response import Response
|
|
from rest_framework.views import APIView
|
|
|
|
from api.models import Apps, AppReleaseInfo, UserInfo, AppScreenShot, CertificationInfo, UserAdDisplayInfo
|
|
from api.tasks import run_resign_task
|
|
from api.utils.TokenManager import verify_token
|
|
from api.utils.app.apputils import get_random_short, save_app_infos
|
|
from api.utils.auth import ExpiringTokenAuthentication
|
|
from api.utils.baseutils import make_app_uuid, make_from_user_uuid
|
|
from api.utils.modelutils import get_app_domain_name, get_redirect_server_domain, check_super_sign_permission
|
|
from api.utils.response import BaseResponse
|
|
from api.utils.storage.caches import upload_file_tmp_name, del_cache_response_by_short
|
|
from api.utils.storage.storage import Storage
|
|
from fir_ser import settings
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class AppAnalyseView(APIView):
|
|
authentication_classes = [ExpiringTokenAuthentication, ]
|
|
|
|
def post(self, request):
|
|
"""
|
|
应用上传前 分析数据,并返回应用上传信息
|
|
:param request:
|
|
:return:
|
|
"""
|
|
res = BaseResponse()
|
|
# 1.接收 bundelid ,返回随机应用名称和短连接
|
|
bundle_id = request.data.get("bundleid", None)
|
|
app_type = request.data.get("type", None)
|
|
|
|
if bundle_id and app_type:
|
|
ap = 'apk'
|
|
if app_type.lower() == 'iOS'.lower():
|
|
ap = 'ipa'
|
|
app_uuid = make_app_uuid(request.user, bundle_id + ap)
|
|
uid = request.user.uid
|
|
release_id = make_from_user_uuid(uid)
|
|
png_id = make_from_user_uuid(uid)
|
|
app_obj = Apps.objects.filter(app_id=app_uuid).first()
|
|
binary_url = ''
|
|
enable_sign = False
|
|
if app_obj:
|
|
is_new = False
|
|
enable_sign = app_obj.issupersign
|
|
short = app_obj.short
|
|
app_release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first()
|
|
short_domain_name = get_redirect_server_domain(request, request.user, get_app_domain_name(app_obj))
|
|
if app_release_obj:
|
|
binary_url = app_release_obj.binary_url
|
|
else:
|
|
is_new = True
|
|
short_domain_name = get_redirect_server_domain(request, request.user)
|
|
short = get_random_short()
|
|
if app_type == 'iOS':
|
|
upload_key = release_id + '.ipa' + settings.FILE_UPLOAD_TMP_KEY
|
|
else:
|
|
upload_key = release_id + '.apk' + settings.FILE_UPLOAD_TMP_KEY
|
|
png_key = png_id + '.png' + settings.FILE_UPLOAD_TMP_KEY
|
|
storage = Storage(request.user)
|
|
storage_type = storage.get_storage_type()
|
|
upload_token = storage.get_upload_token(upload_key)
|
|
png_token = storage.get_upload_token(png_key)
|
|
upload_file_tmp_name("set", png_key, request.user.id)
|
|
upload_file_tmp_name("set", upload_key, request.user.id)
|
|
res.data = {"app_uuid": app_uuid, "short": short,
|
|
"short_domain_name": short_domain_name,
|
|
"upload_token": upload_token,
|
|
"upload_key": upload_key,
|
|
"png_token": png_token,
|
|
"png_key": png_key,
|
|
"storage": storage_type,
|
|
"is_new": is_new,
|
|
"binary_url": binary_url,
|
|
"sign": check_super_sign_permission(request.user),
|
|
"enable_sign": enable_sign
|
|
}
|
|
if storage_type not in [1, 2]:
|
|
res.data['domain_name'] = settings.SERVER_DOMAIN.get("FILE_UPLOAD_DOMAIN", None)
|
|
else:
|
|
res.code = 1003
|
|
|
|
return Response(res.dict)
|
|
|
|
def put(self, request):
|
|
"""
|
|
该方法就是 应用上传完成之后的回调方法,更新或者创建新应用信息
|
|
:param request:
|
|
:return:
|
|
"""
|
|
res = BaseResponse()
|
|
data = request.data
|
|
app_info = {
|
|
"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", ''),
|
|
"udid": data.get("udid", ''),
|
|
"distribution_name": data.get("distribution_name", ''),
|
|
}
|
|
|
|
try:
|
|
storage = Storage(request.user)
|
|
app_tmp_filename = data.get("upload_key")
|
|
app_new_filename = data.get("upload_key").strip(settings.FILE_UPLOAD_TMP_KEY)
|
|
|
|
png_tmp_filename = data.get("png_key")
|
|
png_new_filename = data.get("png_key").strip(settings.FILE_UPLOAD_TMP_KEY)
|
|
logger.info(f"user {request.user} create or update app {data.get('bundleid')} data:{data}")
|
|
if save_app_infos(app_new_filename, request.user, app_info,
|
|
data.get("bundleid"), png_new_filename, data.get("short"), data.get('filesize'),
|
|
data.get('enable_sign')):
|
|
# 需要将tmp 文件修改为正式文件
|
|
storage.rename_file(app_tmp_filename, app_new_filename)
|
|
storage.rename_file(png_tmp_filename, png_new_filename)
|
|
|
|
app_obj = Apps.objects.filter(bundle_id=data.get("bundleid"), user_id=request.user).first()
|
|
if app_obj:
|
|
if app_obj.issupersign and app_obj.user_id.supersign_active:
|
|
c_task = run_resign_task.apply_async((app_obj.app_id, False, False))
|
|
msg = c_task.get(propagate=False)
|
|
logger.info(f"app {app_obj} run_resign_task msg:{msg}")
|
|
if c_task.successful():
|
|
c_task.forget()
|
|
else:
|
|
storage.delete_file(app_tmp_filename)
|
|
storage.delete_file(png_tmp_filename)
|
|
res.code = 10003
|
|
res.msg = '应用信息保存失败'
|
|
# 删除redis key
|
|
upload_file_tmp_name("del", app_tmp_filename, request.user.id)
|
|
upload_file_tmp_name("del", png_tmp_filename, request.user.id)
|
|
|
|
except Exception as e:
|
|
logger.error(f"user {request.user} save app {data.get('bundleid')} info {app_info} failed Exception:{e}")
|
|
res.code = 10003
|
|
res.msg = '应用信息保存失败'
|
|
|
|
return Response(res.dict)
|
|
|
|
|
|
class UploadView(APIView):
|
|
"""
|
|
上传文件接口
|
|
"""
|
|
authentication_classes = [ExpiringTokenAuthentication, ]
|
|
|
|
def get(self, request):
|
|
"""
|
|
该方法 主要是本地上传,通过该方法获取上传的 应用图片或者用户头像 的上传信息
|
|
:param request:
|
|
:return:
|
|
"""
|
|
res = BaseResponse()
|
|
storage = Storage(request.user)
|
|
request_upload_key = request.query_params.get("upload_key", None)
|
|
app_id = request.query_params.get("app_id", None)
|
|
f_type = request.query_params.get("ftype", None)
|
|
if f_type and app_id and request_upload_key:
|
|
if f_type == 'app' or f_type == 'screen':
|
|
app_obj = Apps.objects.filter(app_id=app_id, user_id=request.user).first()
|
|
|
|
elif f_type and f_type in ['head', 'certification', 'advert']:
|
|
if request.user.uid != app_id:
|
|
res.code = 1007
|
|
res.msg = '该用户不存在'
|
|
return Response(res.dict)
|
|
else:
|
|
app_obj = True
|
|
else:
|
|
app_obj = False
|
|
|
|
if app_obj:
|
|
app_type = request_upload_key.split(".")[-1]
|
|
if app_type not in ["apk", "ipa", 'png', 'jpeg', 'jpg']:
|
|
res.code = 1006
|
|
res.msg = '类型不允许'
|
|
else:
|
|
if f_type and f_type == 'certification':
|
|
storage = Storage(request.user, None, True)
|
|
upload_key = make_from_user_uuid(request.user.uid) + '.' + app_type + settings.FILE_UPLOAD_TMP_KEY
|
|
upload_token = storage.get_upload_token(upload_key)
|
|
storage_type = storage.get_storage_type()
|
|
res.data = {
|
|
"domain_name": settings.SERVER_DOMAIN.get("FILE_UPLOAD_DOMAIN", None),
|
|
"upload_token": upload_token,
|
|
"upload_key": upload_key,
|
|
"storage": storage_type,
|
|
"app_id": app_id,
|
|
"ftype": f_type
|
|
}
|
|
else:
|
|
res.code = 1006
|
|
res.msg = '该应用不存在'
|
|
else:
|
|
res.code = 1006
|
|
return Response(res.dict)
|
|
|
|
def post(self, request):
|
|
"""
|
|
该方法 主要是本地上传文件接口,负责上传 应用图片,应用文件或者用户头像
|
|
:param request:
|
|
:return:
|
|
"""
|
|
res = BaseResponse()
|
|
|
|
# 获取多个file
|
|
files = request.FILES.getlist('file', None)
|
|
cert_info = request.data.get('certinfo', None)
|
|
|
|
try:
|
|
cert_info = json.loads(cert_info)
|
|
if not cert_info:
|
|
res.msg = "数据信息 校验失败"
|
|
res.code = 1006
|
|
return Response(res.dict)
|
|
except Exception as e:
|
|
logger.error(f"{request.user} cert_info:{cert_info} get failed Exception:{e}")
|
|
res.msg = "token 校验失败"
|
|
res.code = 1006
|
|
return Response(res.dict)
|
|
|
|
if verify_token(token=cert_info.get("upload_token", None),
|
|
release_id=cert_info.get("upload_key", None)):
|
|
|
|
app_id = cert_info.get("app_id", None)
|
|
f_type = cert_info.get("ftype", None)
|
|
if f_type and f_type in ['app', 'screen']:
|
|
pass
|
|
# app_obj = Apps.objects.filter(app_id=app_id, user_id=request.user).first()
|
|
# if not app_obj:
|
|
# res.code = 1006
|
|
# res.msg = '该应用不存在'
|
|
# return Response(res.dict)
|
|
elif f_type and f_type in ['head', 'certification', 'advert']:
|
|
if request.user.uid != app_id:
|
|
res.code = 1007
|
|
res.msg = '该用户不存在'
|
|
return Response(res.dict)
|
|
else:
|
|
res.code = 1008
|
|
res.msg = '该请求不存在'
|
|
return Response(res.dict)
|
|
if not files:
|
|
res.msg = "文件不存在"
|
|
for file_obj in files:
|
|
try:
|
|
|
|
app_type = file_obj.name.split(".")[-1]
|
|
if app_type == "tmp":
|
|
app_type = file_obj.name.split(".")[-2]
|
|
if app_type not in ["apk", "ipa", 'png', 'jpeg', 'jpg']:
|
|
logger.error(f"user:{request.user} upload file type error file:{file_obj.name}")
|
|
raise TypeError
|
|
except Exception as e:
|
|
logger.error(f"user:{request.user} upload file type error Exception:{e}")
|
|
res.code = 1003
|
|
res.msg = "错误的类型"
|
|
return Response(res.dict)
|
|
|
|
random_file_name = make_from_user_uuid(request.user.uid)
|
|
local_file = os.path.join(settings.MEDIA_ROOT, cert_info.get("upload_key", random_file_name))
|
|
# 读取传入的文件
|
|
logger.info(f"user:{request.user} save file:{local_file}")
|
|
try:
|
|
destination = open(local_file, 'wb+')
|
|
for chunk in file_obj.chunks():
|
|
destination.write(chunk)
|
|
destination.close()
|
|
|
|
except Exception as e:
|
|
logger.error(f"user:{request.user} save file:{local_file} error Exception:{e}")
|
|
res.code = 1003
|
|
res.msg = "数据写入失败"
|
|
try:
|
|
if os.path.isfile(local_file):
|
|
os.remove(local_file)
|
|
except Exception as e:
|
|
logger.error(f"user:{request.user} delete file:{local_file} error Exception:{e}")
|
|
return Response(res.dict)
|
|
else:
|
|
res.msg = "token 校验失败"
|
|
res.code = 1006
|
|
|
|
return Response(res.dict)
|
|
|
|
def put(self, request):
|
|
"""
|
|
该方法就是 应用图片或者用户头像上传完成之后的回调方法,为了更新上传完成的信息
|
|
:param request:
|
|
:return:
|
|
"""
|
|
res = BaseResponse()
|
|
cert_info = request.data.get('certinfo', None)
|
|
if cert_info:
|
|
app_id = cert_info.get("app_id", None)
|
|
logger.info(f"user {request.user} update img {app_id} info")
|
|
|
|
f_type = cert_info.get('ftype', None)
|
|
upload_key = cert_info.get("upload_key", None)
|
|
if f_type and upload_key:
|
|
storage = Storage(request.user)
|
|
new_upload_key = upload_key.strip(settings.FILE_UPLOAD_TMP_KEY)
|
|
else:
|
|
res.msg = '参数有误'
|
|
res.code = 1008
|
|
return Response(res.dict)
|
|
if f_type and app_id and f_type == 'app':
|
|
app_obj = Apps.objects.filter(app_id=app_id, user_id=request.user).first()
|
|
if app_obj:
|
|
release_obj = AppReleaseInfo.objects.filter(app_id=app_obj, is_master=True).first()
|
|
if release_obj:
|
|
old_file_key = release_obj.icon_url
|
|
release_obj.icon_url = new_upload_key
|
|
release_obj.save(update_fields=["icon_url"])
|
|
storage.rename_file(upload_key, new_upload_key)
|
|
del_cache_response_by_short(app_id)
|
|
storage.delete_file(old_file_key)
|
|
return Response(res.dict)
|
|
elif f_type and app_id and f_type == 'screen':
|
|
app_obj = Apps.objects.filter(app_id=app_id, user_id=request.user).first()
|
|
if app_obj:
|
|
if AppScreenShot.objects.filter(app_id=app_obj).count() >= 5:
|
|
storage.delete_file(upload_key)
|
|
res.msg = '最多支持五张截图'
|
|
res.code = 1009
|
|
return Response(res.dict)
|
|
AppScreenShot.objects.create(app_id=app_obj, screenshot_url=new_upload_key)
|
|
storage.rename_file(upload_key, new_upload_key)
|
|
del_cache_response_by_short(app_id)
|
|
return Response(res.dict)
|
|
|
|
elif f_type and app_id and f_type in ['head', 'certification', 'advert']:
|
|
if request.user.uid != app_id:
|
|
res.code = 1007
|
|
res.msg = '该用户不存在'
|
|
return Response(res.dict)
|
|
if f_type == 'head':
|
|
old_file_key = request.user.head_img
|
|
UserInfo.objects.filter(pk=request.user.id).update(head_img=new_upload_key)
|
|
if old_file_key != "" or old_file_key != 'head_img.jpeg':
|
|
storage.delete_file(old_file_key)
|
|
storage.rename_file(upload_key, new_upload_key)
|
|
elif f_type == 'certification':
|
|
ext = cert_info.get('ext', None)
|
|
if ext:
|
|
p_type = ext.get('ptype', None)
|
|
if p_type is not None and p_type in [1, 2, 3]:
|
|
storage = Storage(request.user, None, True)
|
|
certification_obj = CertificationInfo.objects.filter(user_id=request.user,
|
|
type=p_type).first()
|
|
if certification_obj:
|
|
old_certification_url = certification_obj.certification_url
|
|
certification_obj.certification_url = new_upload_key
|
|
certification_obj.save(update_fields=["certification_url"])
|
|
storage.delete_file(old_certification_url)
|
|
else:
|
|
CertificationInfo.objects.create(user_id=request.user, type=p_type,
|
|
certification_url=new_upload_key)
|
|
storage.rename_file(upload_key, new_upload_key)
|
|
|
|
return Response(res.dict)
|
|
elif f_type == 'advert':
|
|
ext = cert_info.get('ext', None)
|
|
if ext:
|
|
pk = ext.get('id')
|
|
if pk:
|
|
ad_info_obj = UserAdDisplayInfo.objects.filter(user_id=request.user, pk=pk).first()
|
|
if ad_info_obj:
|
|
old_file_key = ad_info_obj.ad_pic
|
|
storage.delete_file(old_file_key)
|
|
storage.rename_file(upload_key, new_upload_key)
|
|
ad_info_obj.ad_pic = new_upload_key
|
|
ad_info_obj.save(update_fields=['ad_pic'])
|
|
return Response(res.dict)
|
|
else:
|
|
pass
|
|
res.code = 1008
|
|
return Response(res.dict)
|
|
|