增加登录验证码操作,登录失败超过限制次数,锁定24小时

super_signature
nineven 5 years ago
parent fb99233ce5
commit d9b0d3718c
  1. 5
      fir_client/src/components/FirAppInfosBase.vue
  2. 11
      fir_client/src/components/FirAppInfosbaseinfo.vue
  3. 11
      fir_client/src/components/FirAppInfoscombo.vue
  4. 5
      fir_client/src/components/FirAppInfossecurity.vue
  5. 14
      fir_client/src/components/FirAppInfostimeline.vue
  6. 31
      fir_client/src/components/FirApps.vue
  7. 4
      fir_client/src/components/FirDownload.vue
  8. 109
      fir_client/src/components/FirLogin.vue
  9. 82
      fir_client/src/restful/index.js
  10. 5
      fir_ser/api/urls.py
  11. 28
      fir_ser/api/utils/storage/caches.py
  12. 15
      fir_ser/api/utils/storage/localApi.py
  13. 28
      fir_ser/api/utils/utils.py
  14. 131
      fir_ser/api/views/apps.py
  15. 18
      fir_ser/api/views/captcha.py
  16. 5
      fir_ser/api/views/download.py
  17. 81
      fir_ser/api/views/login.py
  18. 63
      fir_ser/fir_ser/settings.py
  19. 9
      fir_ser/fir_ser/urls.py
  20. 2
      fir_ser/nginx-vhost.conf
  21. 2
      fir_ser/requirements.txt

@ -64,7 +64,7 @@
</template>
<script>
import {getappinfos} from "../restful";
import {apputils} from "../restful";
export default {
name: "FirAppInfosBase",
@ -136,7 +136,7 @@
},
},
computed: {}, mounted() {
getappinfos(data => {
apputils(data => {
if (data.code === 1000) {
this.appinfos = data.data;
@ -151,6 +151,7 @@
console.log("失败了");
}
}, {
"methods": "GET",
"app_id": this.$route.params.id
});
if (this.$store.state.currentapp.master_release) {

@ -58,7 +58,7 @@
</template>
<script>
import {deleteapp, getappinfos, getapppicurl, uploadimgs, updateapp, getuploadurl} from "../restful"
import {apputils, getapppicurl, uploadimgs, getuploadurl} from "../restful"
import {uploadaliyunoss, uploadlocalstorage, uploadqiniuoss} from "../utils";
export default {
@ -122,7 +122,7 @@
// eslint-disable-next-line no-unused-vars
.then(res => {
this.willDeleteApp = false;
deleteapp(data => {
apputils(data => {
if (data.code === 1000) {
this.$message({
message: '删除成功',
@ -136,6 +136,7 @@
});
}
}, {
"methods": "DELETE",
"app_id": this.currentapp.app_id
});
@ -147,7 +148,7 @@
// alert('APP',this.delapp.name)
},
updateinfo() {
getappinfos(data => {
apputils(data => {
if (data.code === 1000) {
this.appinfos = data.data;
@ -163,17 +164,19 @@
console.log("失败了");
}
}, {
"methods": "GET",
"app_id": this.currentapp.app_id
});
},
saveappinfo() {
updateapp(data => {
apputils(data => {
if (data.code === 1000) {
this.$message.success('数据更新成功');
} else {
this.$message.error('操作失败,' + data.msg);
}
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": {
"description": this.currentapp.description,

@ -80,7 +80,7 @@
</template>
<script>
import {getappinfos, getapps, updateapp} from "../restful";
import {getapps, apputils} from "../restful";
export default {
name: "FirAppInfoscombo"
@ -111,13 +111,13 @@
}
},
each_add(hcombo_id) {
updateapp(data => {
apputils(data => {
if (data.code === 1000) {
this.$message.success('操作成功');
this.comboapplists = [];
getappinfos(data => {
apputils(data => {
let appinfos = {};
appinfos = data.data;
appinfos["icon_url"] = appinfos.master_release.icon_url;
@ -134,6 +134,7 @@
}
}, {
"methods": "GET",
"app_id": this.currentapp.app_id
});
@ -142,6 +143,7 @@
}
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": {
"has_combo": {
@ -152,7 +154,7 @@
});
},
each_confirm() {
updateapp(data => {
apputils(data => {
if (data.code === 1000) {
this.$message.success('操作成功');
@ -166,6 +168,7 @@
}
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": {
"has_combo": {

@ -68,7 +68,7 @@
</template>
<script>
import {updateapp,} from "../restful"
import {apputils,} from "../restful"
import {deepCopy} from "../utils";
export default {
@ -92,7 +92,7 @@
this.currentapp.count = 0;
},
saveappinfo(data) {
updateapp(data => {
apputils(data => {
if (data.code === 1000) {
this.$message.success('数据更新成功');
} else {
@ -106,6 +106,7 @@
}
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": data
});

@ -115,7 +115,7 @@
</template>
<script>
import {deletereleaseapp, getapptimeline, getdownloadurl, updatereleaseapp} from "../restful"
import {releaseapputils, getdownloadurl} from "../restful"
export default {
name: "FirAppInfostimeline",
@ -161,7 +161,7 @@
spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)'
});
getapptimeline(data => {
releaseapputils(data => {
if (data.code === 1000) {
this.release_apps = data.data.release_apps;
this.currentapp = data.data.currentapp;
@ -171,8 +171,9 @@
}
loading.close();
}, {
"methods": "GET",
"app_id": this.$route.params.id,
"action": "timeline",
"release_id": "timeline",
})
},
del_release_app(app) {
@ -180,7 +181,7 @@
this.$confirm('确认删除 ' + this.currentapp.name + '下 当前 release 版本吗?')
// eslint-disable-next-line no-unused-vars
.then(res => {
deletereleaseapp(data => {
releaseapputils(data => {
if (data.code === 1000) {
this.$message({
message: '删除成功',
@ -195,6 +196,7 @@
});
}
}, {
"methods": "DELETE",
"app_id": this.currentapp.app_id,
"release_id": app.release_id,
});
@ -202,7 +204,7 @@
},
updatereleaseappFun(params) {
updatereleaseapp(data => {
releaseapputils(data => {
if (data.code === 1000) {
this.$message({
message: '更新成功',
@ -225,6 +227,7 @@
},
make_master_release(app) {
this.updatereleaseappFun({
"methods": "PUT",
"app_id": this.currentapp.app_id,
"release_id": app.release_id,
"data": {
@ -250,6 +253,7 @@
this.updatas = {"binary_url": app.binary_url}
}
this.updatereleaseappFun({
"methods": "PUT",
"app_id": this.currentapp.app_id,
"release_id": app.release_id,
"data": this.updatas

@ -181,23 +181,23 @@
<el-col :span="4">
<el-row>
<el-col :span="20">
<el-input
placeholder="请输入名称搜索"
v-model="keysearch"
@click="searchapps"
clearable >
</el-input>
<el-input
placeholder="请输入名称搜索"
v-model="keysearch"
@click="searchapps"
clearable>
</el-input>
</el-col>
<el-col :span="2" >
<el-col :span="2">
<el-button icon="el-icon-search" @click="searchFun">
</el-button>
<el-button icon="el-icon-search" @click="searchFun">
</el-button>
</el-col>
</el-row>
</el-col>
<!-- <el-col :span="1" class="surplus-card">-->
<!-- -->
<!-- </el-col>-->
<!-- <el-col :span="1" class="surplus-card">-->
<!-- -->
<!-- </el-col>-->
<el-col :span="7" class="surplus-card">
<el-row>
@ -405,7 +405,7 @@
</template>
<script>
import {getapps, deleteapp, analyseApps, getuploadurl} from "../restful";
import {getapps, apputils, analyseApps, getuploadurl} from "../restful";
import {
getScrollHeight,
getScrollTop,
@ -698,7 +698,7 @@
},
delApp() {
this.willDeleteApp = false;
deleteapp(data => {
apputils(data => {
if (data.code === 1000) {
for (let i = 0; i < this.applists.length; i++) {
if (this.delapp.app_id === this.applists[i].app_id) {
@ -712,6 +712,7 @@
this.$message.error('删除失败,请联系管理员');
}
}, {
"methods": "DELETE",
"app_id": this.delapp.app_id
});
},
@ -794,7 +795,7 @@
keysearch: function (val, oldVal) {
// this.searchapps()
let keysearch = this.keysearch.replace(/^\s+|\s+$/g, "");
if(keysearch === ""){
if (keysearch === "") {
this.searchFun()
}
},

@ -13,8 +13,8 @@
</div>
<div v-else>
<!-- <span class="pattern left"><img src="../assets/download_pattern_left.png"></span>-->
<!-- <span class="pattern right"><img src="../assets/download_pattern_right.png"></span>-->
<!-- <span class="pattern left"><img src="../assets/download_pattern_left.png"></span>-->
<!-- <span class="pattern right"><img src="../assets/download_pattern_right.png"></span>-->
</div>
<el-container class="out-container">

@ -24,6 +24,20 @@
<el-input v-model="form.password" prefix-icon="el-icon-lock" placeholder="密码"
show-password></el-input>
</el-form-item>
<el-form-item style="height: 40px">
<el-row style="height: 40px">
<el-col :span="16">
<el-input placeholder="请输入验证码" v-model="form.authcode" maxlength="6"></el-input>
</el-col>
<el-col :span="8">
<el-image
style="margin:0px 4px;border-radius:4px;cursor:pointer;height: 40px"
:src="cptch.cptch_image"
fit="contain" @click="get_auth_code">
</el-image>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
@ -53,49 +67,61 @@
form: {
email: '',
password: '',
authcode:''
},
cptch:{"cptch_image": '', "cptch_key": '',"length":8},
}
},
methods: {
onSubmit() {
let email = this.form.email;
let password = this.form.password;
if (this.isEmail(email) && password.length > 6) {
// alert(email,password);
loginFun(data => {
if (data.code == 1000) {
this.$message({
message: '登录成功',
type: 'success'
});
this.$cookies.remove("auth_token");
this.$cookies.set("token", data['token']);
this.$cookies.set("username", data.userinfo.username);
this.$cookies.set("first_name", data.userinfo.first_name);
this.$store.dispatch("get_user", data.userinfo);
set_auth_token();
this.$router.push({name: 'FirApps'})
} else {
this.$message({
message: data.msg,
type: 'error'
});
}
}, {
"username": email,
"password": password
});
} else {
let authcode = this.form.authcode;
if(authcode.length === this.cptch.length){
if (this.isEmail(email) && password.length > 6) {
loginFun(data => {
if (data.code == 1000) {
this.$message({
message: '登录成功',
type: 'success'
});
this.$cookies.remove("auth_token");
this.$cookies.set("token", data['token']);
this.$cookies.set("username", data.userinfo.username);
this.$cookies.set("first_name", data.userinfo.first_name);
this.$store.dispatch("get_user", data.userinfo);
set_auth_token();
this.$router.push({name: 'FirApps'})
} else {
this.$message({
message: data.msg,
type: 'error'
});
this.get_auth_code();
}
}, {
"methods":"POST",
"data":{
"username": email,
"password": password,
"authcode":authcode,
"cptch_key":this.cptch.cptch_key
}
});
} else {
this.$message({
message: '请输入正确的邮箱地址',
type: 'warning'
});
}
}else {
this.$message({
message: '请输入正确的邮箱地址',
message: '验证码有误',
type: 'warning'
});
}
},
onRegister() {
this.$router.push({name: 'FirRegist'})
@ -105,10 +131,25 @@
return true;
}
return false;
}
},
get_auth_code(){
loginFun(data => {
if (data.code == 1000) {
this.cptch=data.data;
} else {
this.$message({
message: data.msg,
type: 'error'
});
}
}, {
"methods":"GET",
"data":{}
});
},
},
mounted() {
this.get_auth_code();
}, created() {
}
}

@ -88,7 +88,7 @@ function ErrorMsg(error) {
}
if (error.response && error.response.status === 403) {
router.push({name: 'FirLogin'});
}else{
} else {
if (error.message === 'Network Error') {
alert('网络连接失败');
} else {
@ -161,9 +161,9 @@ function getData(methods = true, url, params = {}, callBack, load, isCode = fals
/**用户登录 */
export function loginFun(callBack, params, load = true) {
getData(
'POST',
params.methods,
USERSEVER + '/login',
params,
params.data,
data => {
callBack(data);
},
@ -204,58 +204,11 @@ export function getapps(callBack, params, load = true) {
);
}
/**用户应用详情 */
export function getappinfos(callBack, params, load = true) {
getData(
'GET',
USERSEVER + '/apps/' + params.app_id,
{},
data => {
callBack(data);
},
load,
true,
true
);
}
/**删除应用 */
export function deleteapp(callBack, params, load = true) {
getData(
"DELETE",
USERSEVER + '/apps/' + params.app_id,
{},
data => {
callBack(data);
},
load,
true,
true
);
}
/**删除release应用 */
export function deletereleaseapp(callBack, params, load = true) {
/**app应用操作 */
export function apputils(callBack, params, load = true) {
getData(
"DELETE",
USERSEVER + '/appinfos/' + params.app_id + '/' + params.release_id,
{},
data => {
callBack(data);
},
load,
true,
true
);
}
/**更新app应用 */
export function updateapp(callBack, params, load = true) {
getData(
"PUT",
params.methods,
USERSEVER + '/apps/' + params.app_id,
params.data,
data => {
@ -267,11 +220,10 @@ export function updateapp(callBack, params, load = true) {
);
}
/**更新release应用 */
export function updatereleaseapp(callBack, params, load = true) {
/**release应用操作 */
export function releaseapputils(callBack, params, load = true) {
getData(
"PUT",
params.methods,
USERSEVER + '/appinfos/' + params.app_id + '/' + params.release_id,
params.data,
data => {
@ -284,22 +236,6 @@ export function updatereleaseapp(callBack, params, load = true) {
}
/**用户应用详情 */
export function getapptimeline(callBack, params, load = true) {
getData(
'GET',
USERSEVER + '/appinfos/' + params.app_id + '/' + params.action,
{},
data => {
callBack(data);
},
load,
true,
true
);
}
/**根据短链接获取应用信息 */
export function getShortAppinfo(callBack, params, load = true) {
getData(

@ -18,7 +18,7 @@ from django.urls import path, re_path, include
from api.views.login import LoginView, UserInfoView
from api.views.logout import LogoutView
from api.views.captcha import CaptchaView
from api.views.captcha import AjaxExampleForm
from api.views.apps import AppsView, AppInfoView, AppReleaseinfoView
from api.views.download import ShortDownloadView
from api.views.uploads import AppAnalyseView, UploadView
@ -34,7 +34,7 @@ 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("^captcha_check/", AjaxExampleForm.as_view()),
re_path("^apps$", AppsView.as_view()),
re_path("^storage$", StorageView.as_view()),
re_path("^apps/(?P<app_id>\w+)", AppInfoView.as_view()),
@ -47,4 +47,5 @@ urlpatterns = [
re_path("^supersign/developer$", DeveloperView.as_view()),
re_path("^supersign/devices$", SuperSignUsedView.as_view()),
re_path("^supersign/udid$", AppUDIDUsedView.as_view()),
]

@ -8,11 +8,13 @@ from django.core.cache import cache
from api.models import Apps, UserInfo, AppReleaseInfo, AppUDID, APPToDeveloper, APPSuperSignUsedInfo
import time, os
from django.utils import timezone
from fir_ser.settings import CACHE_KEY_TEMPLATE, SERVER_DOMAIN
from fir_ser.settings import CACHE_KEY_TEMPLATE, SERVER_DOMAIN, SYNC_CACHE_TO_DATABASE
from api.utils.storage.storage import Storage, LocalStorage
from api.utils.crontab.sync_cache import sync_download_times_by_app_id
from api.utils.utils import file_format_path
import logging
logger = logging.getLogger(__file__)
try:
from api.utils.crontab import run
except Exception as e:
@ -180,3 +182,27 @@ def upload_file_tmp_name(act, filename, user_obj_id):
return cache.get(tmp_key)
elif act == "del":
cache.delete(tmp_key)
def login_auth_failed(act, email):
auth_code_key = "_".join([CACHE_KEY_TEMPLATE.get("login_failed_try_times_key"), email])
if act == "set":
data = {
"count": 1,
"time": time.time()
}
cdata = cache.get(auth_code_key)
if cdata:
data["count"] = cdata["count"] + 1
data["time"] = time.time()
logger.info("auth_code_key:%s data:%s" % (auth_code_key, data))
cache.set(auth_code_key, data, 60 * 60)
elif act == "get":
cdata = cache.get(auth_code_key)
if cdata:
if cdata["count"] > SYNC_CACHE_TO_DATABASE.get("try_login_times"):
logging.error("email:%s login failed too many ,is locked . cdata:%s" % (email, cdata))
return False
return True
elif act == "del":
cache.delete(auth_code_key)

@ -20,17 +20,18 @@ class LocalStorage(object):
dtoken = DownloadToken()
return dtoken.make_token(name, expires)
def get_download_url(self, name, expires=600, force_new=False):
dtoken = DownloadToken()
base_url = '/'.join([self.domain_name, 'download', name])
def get_base_url(self):
uri = 'http://'
if self.is_https:
uri = 'https://'
return "%s%s" % (uri, self.domain_name)
def get_download_url(self, name, expires=600, force_new=False):
dtoken = DownloadToken()
download_url = '/'.join([self.get_base_url(), 'download', name])
if settings.DATA_DOWNLOAD_KEY_OPEN:
download_url = "%s%s?%s=%s" % (
uri, base_url, settings.DATA_DOWNLOAD_KEY, dtoken.make_token(name, expires, force_new=force_new))
else:
download_url = "%s%s" % (uri, base_url)
download_url = "%s?%s=%s" % (
download_url, settings.DATA_DOWNLOAD_KEY, dtoken.make_token(name, expires, force_new=force_new))
return download_url
def del_file(self, name):

@ -4,12 +4,17 @@
# author: liuyu
# date: 2020/5/7
import os
from fir_ser.settings import SUPER_SIGN_ROOT
from api.models import APPSuperSignUsedInfo, AppUDID, AppIOSDeveloperInfo, AppReleaseInfo, Apps, APPToDeveloper, \
from fir_ser.settings import SUPER_SIGN_ROOT, SERVER_DOMAIN, CAPTCHA_LENGTH
from api.models import APPSuperSignUsedInfo, APPToDeveloper, \
UDIDsyncDeveloper
from api.utils.app.randomstrings import make_app_uuid
from api.utils.storage.localApi import LocalStorage
from django.db.models import Sum
from captcha.models import CaptchaStore
from captcha.helpers import captcha_image_url
import logging
logger = logging.getLogger(__name__)
def file_format_path(user_obj, auth=None, email=None):
@ -77,3 +82,22 @@ def get_developer_devices(developer_obj_lists):
"flyapp_used_sum": flyapp_used_sum,
}
return use_num
def get_captcha():
cptch_key = CaptchaStore.generate_key()
cptch_image = captcha_image_url(cptch_key)
CaptchaStore.remove_expired()
local_storage = LocalStorage(**SERVER_DOMAIN.get("IOS_PMFILE_DOWNLOAD_DOMAIN"))
return {"cptch_image": "/".join([local_storage.get_base_url(), cptch_image.strip("/")]), "cptch_key": cptch_key,
"length": CAPTCHA_LENGTH}
def valid_captcha(cptch_key, code, username):
if username:
challenge = CaptchaStore.objects.filter(hashkey=cptch_key).values("challenge").first()
logger.info("cptch_key:%s code:%s challenge:%s" % ((cptch_key, code, challenge)))
if challenge:
if cptch_key and code and code.strip(" ").lower() == challenge.get("challenge").lower():
return True
return False

@ -18,6 +18,9 @@ from api.utils.storage.caches import del_cache_response_by_short, get_app_today_
from api.models import Apps, AppReleaseInfo, APPToDeveloper, AppIOSDeveloperInfo, UserInfo
from api.utils.serializer import AppsSerializer, AppReleaseSerializer, UserInfoSerializer
from rest_framework.pagination import PageNumberPagination
import logging
logger = logging.getLogger(__name__)
class AppsPageNumber(PageNumberPagination):
@ -34,7 +37,7 @@ class AppsView(APIView):
app_type = request.query_params.get("type", None)
act_type = request.query_params.get("act", None)
logger.info("app_type:%s act_type:%s" % (app_type, act_type))
res = BaseResponse()
res.hdata = {"all_hits_count": 0}
res.hdata["upload_domain"] = request.user.domain_name
@ -52,9 +55,11 @@ class AppsView(APIView):
count_hits = all_hits_obj.get("count_hits", 0)
if count_hits:
if count_hits > 0:
request.user.all_download_times += count_hits
logger.info(
"update user all_download_times old:%s now:%s" % (
count_hits, request.user.all_download_times))
UserInfo.objects.filter(pk=request.user.id).update(
all_download_times=request.user.all_download_times + count_hits)
all_download_times=count_hits)
else:
count_hits = 0
res.hdata["all_hits_count"] = count_hits
@ -73,7 +78,7 @@ class AppsView(APIView):
filter_data["has_combo"] = None
apps_obj = Apps.objects.filter(**filter_data)
logger.info(filter_data)
page_obj = AppsPageNumber()
app_page_serializer = page_obj.paginate_queryset(queryset=apps_obj.order_by("-updated_time"), request=request,
view=self)
@ -100,6 +105,7 @@ class AppInfoView(APIView):
app_serializer = AppsSerializer(apps_obj, context={"storage": Storage(request.user)})
res.data = app_serializer.data
else:
logger.error("app_id:%s is not found in user:%s " % (app_id, request.user))
res.msg = "未找到该应用"
res.code = 1003
userserializer = UserInfoSerializer(request.user)
@ -116,15 +122,20 @@ class AppInfoView(APIView):
apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
if apps_obj:
if apps_obj.issupersign:
logger.info("app_id:%s is supersign ,so delete this app need clean IOS developer")
IosUtils.clean_app_by_user_obj(apps_obj, request.user)
storage = Storage(request.user)
has_combo = apps_obj.has_combo
if has_combo:
logger.info(
"app_id:%s has_combo ,so delete this app need uncombo and clean del_cache_response_by_short")
del_cache_response_by_short(apps_obj.has_combo.short, apps_obj.has_combo.app_id)
apps_obj.has_combo.has_combo = None
del_cache_response_by_short(apps_obj.short, apps_obj.app_id)
for appreleaseobj in AppReleaseInfo.objects.filter(app_id=apps_obj).all():
logger.info("app_id:%s has_combo ,so delete this app need clean all release,release_id:%s" % (
appreleaseobj.release_id))
storage.delete_file(appreleaseobj.release_id, appreleaseobj.release_type)
storage.delete_file(appreleaseobj.icon_url)
appreleaseobj.delete()
@ -139,6 +150,7 @@ class AppInfoView(APIView):
clean = data.get("clean", None)
if clean:
logger.info("app_id:%s clean:%s ,close supersign should clean_app_by_user_obj" % (app_id, clean))
apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
IosUtils.clean_app_by_user_obj(apps_obj, request.user)
return Response(res.dict)
@ -147,6 +159,7 @@ class AppInfoView(APIView):
if has_combo:
actions = has_combo.get("action", None)
hcombo_id = has_combo.get("hcombo_id", None)
logger.info("app_id:%s actions:%s hcombo_id:%s" % (app_id, actions, hcombo_id))
if actions and hcombo_id:
has_combo = Apps.objects.filter(user_id=request.user, app_id=hcombo_id)
@ -166,6 +179,7 @@ class AppInfoView(APIView):
del_cache_response_by_short(has_combo.first().short, has_combo.first().app_id)
except Exception as e:
logger.error("app_id:%s actions:%s hcombo_id:%s Exception:%s" % (app_id, actions, hcombo_id, e))
res.code = 1004
res.msg = "该应用已经关联"
else:
@ -187,71 +201,72 @@ class AppInfoView(APIView):
# return Response(res.dict)
developer_count = AppIOSDeveloperInfo.objects.filter(user_id=request.user).count()
if developer_count == 0:
logger.error("app_id:%s can't open supersign,owner has no ios developer" % (app_id))
res.code = 1008
res.msg = "超级签开发者不存在,无法开启"
return Response(res.dict)
apps_obj.issupersign = data.get("issupersign", apps_obj.issupersign)
apps_obj.save()
except Exception as e:
print(e)
logger.error("app_id:%s update Exception:%s" % (app_id, e))
res.code = 1005
res.msg = "短连接已经存在"
return Response(res.dict)
def post(self, request, app_id):
res = BaseResponse()
# 获取多个file
files = request.FILES.getlist('file', None)
for file_obj in files:
# 将文件缓存到本地后上传
try:
app_type = file_obj.name.split(".")[-1]
if app_type in ['png', 'jpeg', 'jpg']:
# 上传图片
pass
else:
raise
except Exception as e:
res.code = 1003
res.msg = "错误的类型"
return Response(res.dict)
if app_id:
apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
if apps_obj:
release_obj = AppReleaseInfo.objects.filter(app_id=apps_obj, is_master=True).first()
if release_obj:
random_file_name = make_from_user_uuid(request.user)
old_file_name = os.path.basename(release_obj.icon_url)
local_file = os.path.join(settings.MEDIA_ROOT, "icons",
random_file_name + "." + old_file_name.split(".")[1])
# 读取传入的文件
try:
destination = open(local_file, 'wb+')
for chunk in file_obj.chunks():
# 写入本地文件
destination.write(chunk)
destination.close()
except Exception as e:
res.code = 1003
res.msg = "数据写入失败"
return Response(res.dict)
release_obj.icon_url = release_obj.icon_url.replace(old_file_name.split(".")[0],
random_file_name)
release_obj.save()
del_cache_response_by_short(apps_obj.short, apps_obj.app_id)
storage = Storage(request.user)
storage.delete_file(old_file_name)
else:
res.code = 1003
else:
res.code = 1003
return Response(res.dict)
# def post(self, request, app_id):
# res = BaseResponse()
#
# # 获取多个file
# files = request.FILES.getlist('file', None)
# for file_obj in files:
# # 将文件缓存到本地后上传
# try:
# app_type = file_obj.name.split(".")[-1]
# if app_type in ['png', 'jpeg', 'jpg']:
# # 上传图片
# pass
# else:
# raise
# except Exception as e:
# res.code = 1003
# res.msg = "错误的类型"
# return Response(res.dict)
#
# if app_id:
# apps_obj = Apps.objects.filter(user_id=request.user, app_id=app_id).first()
# if apps_obj:
# release_obj = AppReleaseInfo.objects.filter(app_id=apps_obj, is_master=True).first()
# if release_obj:
#
# random_file_name = make_from_user_uuid(request.user)
# old_file_name = os.path.basename(release_obj.icon_url)
# local_file = os.path.join(settings.MEDIA_ROOT, "icons",
# random_file_name + "." + old_file_name.split(".")[1])
# # 读取传入的文件
# try:
# destination = open(local_file, 'wb+')
# for chunk in file_obj.chunks():
# # 写入本地文件
# destination.write(chunk)
# destination.close()
# except Exception as e:
# res.code = 1003
# res.msg = "数据写入失败"
# return Response(res.dict)
# release_obj.icon_url = release_obj.icon_url.replace(old_file_name.split(".")[0],
# random_file_name)
# release_obj.save()
# del_cache_response_by_short(apps_obj.short, apps_obj.app_id)
#
# storage = Storage(request.user)
# storage.delete_file(old_file_name)
#
# else:
# res.code = 1003
# else:
# res.code = 1003
# return Response(res.dict)
class AppReleaseinfoView(APIView):

@ -16,3 +16,21 @@ class CaptchaView(APIView):
response_str = json.loads(response_str)
return Response({"error_no": 0, "data": response_str})
from captcha.models import CaptchaStore
from captcha.helpers import captcha_image_url
import json
class AjaxExampleForm(APIView):
def get(self, request):
to_json_response = dict()
to_json_response['status'] = 0
to_json_response['new_cptch_key'] = CaptchaStore.generate_key()
to_json_response['new_cptch_image'] = captcha_image_url(to_json_response['new_cptch_key'])
CaptchaStore.remove_expired()
a = CaptchaStore.objects.filter(hashkey=to_json_response['new_cptch_key']).first()
print(a)
return Response(to_json_response)

@ -78,7 +78,10 @@ class DownloadView(APIView):
else:
file_path = os.path.join(settings.MEDIA_ROOT, filename)
try:
response = FileResponse(open(file_path, 'rb'))
if os.path.isfile(file_path):
response = FileResponse(open(file_path, 'rb'))
else:
response = FileResponse()
except Exception as e:
print(e)
response = FileResponse()

@ -6,11 +6,16 @@ from django.core.cache import cache
from rest_framework.views import APIView
import binascii
import os, datetime
from api.utils.utils import get_captcha, valid_captcha
from api.utils.TokenManager import DownloadToken, generateNumericTokenOfLength
from api.utils.auth import ExpiringTokenAuthentication
from api.utils.response import BaseResponse
from django.middleware import csrf
from fir_ser.settings import CACHE_KEY_TEMPLATE
from api.utils.storage.caches import login_auth_failed
import logging
logger = logging.getLogger(__name__)
def get_token(request):
@ -27,54 +32,54 @@ class LoginView(APIView):
receive = request.data
if request.method == 'POST':
# print(receive)
# is_valid = verify(receive)
is_valid = True
# print("is_valid", is_valid)
username = receive.get("username", None)
is_valid = valid_captcha(receive.get("cptch_key", None), receive.get("authcode", None), username)
if is_valid:
username = receive.get("username")
password = receive.get("password")
user = auth.authenticate(username=username, password=password)
print(username, password, user)
if user is not None:
if user.is_active:
# update the token
key = self.generate_key()
now = datetime.datetime.now()
user_info = UserInfo.objects.get(pk=user.pk)
auth_key = "_".join([CACHE_KEY_TEMPLATE.get('user_auth_token_key'), key])
cache.set(auth_key, {'uid': user_info.uid, 'username': user_info.username}, 3600 * 24 * 7)
Token.objects.create(user=user, **{"access_token": key, "created": now})
serializer = UserInfoSerializer(user_info, )
data = serializer.data
response.msg = "验证成功!"
response.userinfo = data
response.token = key
if login_auth_failed("get", username):
password = receive.get("password")
user = auth.authenticate(username=username, password=password)
logger.info("username:%s password:%s" % (username, password))
if user is not None:
if user.is_active:
login_auth_failed("del", username)
# update the token
key = self.generate_key()
now = datetime.datetime.now()
user_info = UserInfo.objects.get(pk=user.pk)
auth_key = "_".join([CACHE_KEY_TEMPLATE.get('user_auth_token_key'), key])
cache.set(auth_key, {'uid': user_info.uid, 'username': user_info.username}, 3600 * 24 * 7)
Token.objects.create(user=user, **{"access_token": key, "created": now})
serializer = UserInfoSerializer(user_info, )
data = serializer.data
response.msg = "验证成功!"
response.userinfo = data
response.token = key
else:
response.msg = "用户被禁用"
response.code = 1005
else:
response.msg = "用户被禁用"
response.code = 1002
login_auth_failed("set", username)
try:
UserInfo.objects.get(username=username)
response.msg = "密码或者账户有误"
response.code = 1002
except UserInfo.DoesNotExist:
response.msg = "用户不存在!"
response.code = 1003
else:
try:
UserInfo.objects.get(username=username)
response.msg = "密码错误或者!"
response.code = 1002
except UserInfo.DoesNotExist:
response.msg = "用户不存在!"
response.code = 1003
response.code = 1006
response.msg = "用户登录失败次数过多,已被锁定,请24小时之后再次尝试"
else:
response.code = 1001
response.msg = "请完成滑动验证!"
response.msg = "验证码有误"
return Response(response.dict)
def get(self, request):
response = BaseResponse()
csrf = get_token(request)
response.data = csrf
response.data = get_captcha()
return Response(response.dict)

@ -38,6 +38,7 @@ INSTALLED_APPS = [
'api.apps.ApiConfig',
'django_apscheduler', # 定时执行任务
'rest_framework',
'captcha',
]
MIDDLEWARE = [
@ -236,6 +237,7 @@ CACHE_KEY_TEMPLATE = {
'download_today_times_key': 'download_today_times',
'developer_auth_code_key': 'developer_auth_code',
'upload_file_tmp_name_key': 'upload_file_tmp_name',
'login_failed_try_times_key': 'login_failed_try_times',
}
DATA_DOWNLOAD_KEY = "d_token"
@ -246,6 +248,7 @@ SYNC_CACHE_TO_DATABASE = {
'download_times': 10, # 下载次数同步时间
'auto_clean_tmp_file_times': 60 * 30, # 定时清理上传失误生成的临时文件
'auto_clean_apscheduler_log': 100000, # 定时清理定时任务执行的日志,该参数为日志保留的数量
'try_login_times': 5, # 当天登录失败次数,超过该失败次数,锁定24小时
}
SERVER_DOMAIN = {
@ -259,3 +262,63 @@ SERVER_DOMAIN = {
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
CAPTCHA_TIMEOUT=5 # Minutes
CAPTCHA_LENGTH= 6 # Chars
BASE_LOG_DIR = os.path.join(BASE_DIR, "logs")
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
'[%(levelname)s][%(message)s]'
},
'simple': {
'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
},
'collect': {
'format': '%(message)s'
}
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'filters': ['require_debug_true'], # 只有在Django debug为True时才在屏幕打印日志
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'TF': {
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler', # 保存到文件,根据时间自动切
'filename': os.path.join(BASE_LOG_DIR, "flyapp_info.log"), # 日志文件
'backupCount': 10, # 备份数为3 xx.log --> xx.log.2018-08-23_00-00-00 --> xx.log.2018-08-24_00-00-00 --> ...
'when': 'D', # 每天一切, 可选值有S/秒 M/分 H/小时 D/天 W0-W6/周(0=周一) midnight/如果没指定时间就默认在午夜
'formatter': 'standard',
'encoding': 'utf-8',
},
'error': {
'level': 'ERROR',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自动切
'filename': os.path.join(BASE_LOG_DIR, "flyapp_err.log"), # 日志文件
'maxBytes': 1024 * 1024 * 5, # 日志大小 50M
'backupCount': 10,
'formatter': 'standard',
'encoding': 'utf-8',
},
},
'loggers': {
'': { # 默认的logger应用如下配置
'handlers': ['TF', 'console', 'error'], # 上线之后可以把'console'移除
'level': 'DEBUG',
'propagate': True,
},
},
}

@ -36,10 +36,10 @@ from api.views.download import DownloadView, InstallView
from api.views.receiveudids import IosUDIDView
urlpatterns = [
path('fly.admin/', admin.site.urls),
re_path('fly.admin/', admin.site.urls),
# path("",include(router.urls)),
path("api/v1/fir/server/", include('api.urls')),
re_path("api/v1/fir/server/", include('api.urls')),
re_path('^captcha/', include('captcha.urls')),
# re_path("shoppingcar/",ShoppingCarView.as_view()),
# re_path("account/",AccountView.as_view()),
# re_path("payment/",PaymentView.as_view()),
@ -47,8 +47,9 @@ urlpatterns = [
# re_path("get_pay_url/",get_pay_url),s
# re_path("api/v1/trade/alipay/",AlipayTradeView),
# media路径配置
re_path(r'files/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
re_path('files/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
re_path("download/(?P<filename>\w+\.\w+)$", DownloadView.as_view(), name="download"),
re_path("install/(?P<app_id>\w+)$", InstallView.as_view(), name="install"),
re_path("^udid/(?P<short>\w+)$", IosUDIDView.as_view()),
]

@ -40,7 +40,7 @@ server
location ~ ^/(download|api|files|udid) {
location ~ ^/(download|api|files|udid|captcha) {
#proxy_pass http://synchrotron;
include uwsgi_params;

@ -20,8 +20,10 @@ decorator==4.4.2
Django==3.0.3
django-apscheduler==0.3.0
django-qrcode==0.3
django-ranged-response==0.2.0
django-redis==4.11.0
django-rest-framework==0.1.0
django-simple-captcha==0.5.12
djangorestframework==3.11.0
dnspython==1.16.0
drf-extensions==0.6.0

Loading…
Cancel
Save