更新Django版本,增加celery定时任务

pull/15/head
youngS 4 years ago
parent 2cb0f1a8f5
commit 179f46b2b3
  1. 40
      fir_ser/api/tasks.py
  2. 2
      fir_ser/api/utils/apple/appleapiv3.py
  3. 11
      fir_ser/api/utils/crontab/ctasks.py
  4. 90
      fir_ser/api/utils/crontab/run.py
  5. 53
      fir_ser/api/utils/crontab/runWin.py
  6. 130
      fir_ser/api/utils/decorators.py
  7. 6
      fir_ser/api/utils/pay/alipay/__init__.py
  8. 5
      fir_ser/api/utils/storage/caches.py
  9. 14
      fir_ser/api/utils/throttle.py
  10. 8
      fir_ser/api/views/__init__.py
  11. 2
      fir_ser/api/views/apps.py
  12. 6
      fir_ser/api/views/download.py
  13. 10
      fir_ser/api/views/uploads.py
  14. 2
      fir_ser/fir_ser/celery.py
  15. 46
      fir_ser/fir_ser/settings.py
  16. 276
      fir_ser/readme.txt
  17. 111
      fir_ser/requirements.txt

@ -6,9 +6,17 @@
from celery import shared_task from celery import shared_task
from django.core.cache import cache from django.core.cache import cache
from celery import Celery
from api.utils.storage.storage import get_local_storage
from api.models import Apps from api.models import Apps
from api.utils.app.supersignutils import IosUtils, resign_by_app_id from api.utils.app.supersignutils import IosUtils, resign_by_app_id
from api.utils.crontab.ctasks import sync_download_times, auto_clean_upload_tmp_file, auto_delete_tmp_file, \
auto_check_ios_developer_active
from api.utils.geetest.geetest_utils import check_bypass_status
app = Celery()
@shared_task @shared_task
@ -41,3 +49,35 @@ def run_resign_task(app_id, need_download_profile=True):
app_obj = Apps.objects.filter(app_id=app_id).first() app_obj = Apps.objects.filter(app_id=app_id).first()
with cache.lock("%s_%s" % ('task_resign', app_id), timeout=60 * 60): with cache.lock("%s_%s" % ('task_resign', app_id), timeout=60 * 60):
return resign_by_app_id(app_obj, need_download_profile) return resign_by_app_id(app_obj, need_download_profile)
@app.task
def start_api_sever_do_clean():
# 启动服务的时候,同时执行下面操作,主要是修改配置存储的时候,需要执行清理,否则会出问题,如果不修改,则无需执行
get_local_storage(clean_cache=True)
check_bypass_status()
@app.task
def sync_download_times_job():
sync_download_times()
@app.task
def check_bypass_status_job():
check_bypass_status()
@app.task
def auto_clean_upload_tmp_file_job():
auto_clean_upload_tmp_file()
@app.task
def auto_delete_tmp_file_job():
auto_delete_tmp_file()
@app.task
def auto_check_ios_developer_active_job():
auto_check_ios_developer_active()

@ -680,7 +680,7 @@ class AppStoreConnectApi(DevicesAPI, BundleIDsAPI, BundleIDsCapabilityAPI, Profi
} }
jwt_encoded = jwt.encode(data, self.p8_private_key, algorithm=self.JWT_ALG, headers=jwt_headers) jwt_encoded = jwt.encode(data, self.p8_private_key, algorithm=self.JWT_ALG, headers=jwt_headers)
headers = { headers = {
'Authorization': 'Bearer %s' % (jwt_encoded.decode('utf-8')) 'Authorization': 'Bearer %s' % jwt_encoded
} }
self.headers = headers self.headers = headers

@ -11,7 +11,6 @@ from api.utils.utils import send_ios_developer_active_status
from django.core.cache import cache from django.core.cache import cache
from fir_ser.settings import CACHE_KEY_TEMPLATE, SYNC_CACHE_TO_DATABASE, SUPER_SIGN_ROOT from fir_ser.settings import CACHE_KEY_TEMPLATE, SYNC_CACHE_TO_DATABASE, SUPER_SIGN_ROOT
import time, os import time, os
from django_apscheduler.models import DjangoJobExecution
import logging import logging
logger = logging.getLogger(__file__) logger = logging.getLogger(__file__)
@ -47,16 +46,6 @@ def auto_clean_upload_tmp_file():
logger.info("auto_clean_upload_tmp_file upload_tem_file_key :%s " % (upload_tem_file_key)) logger.info("auto_clean_upload_tmp_file upload_tem_file_key :%s " % (upload_tem_file_key))
def auto_delete_job_log():
job_execution = DjangoJobExecution.objects.order_by("-id").values("id").first()
if job_execution:
need_count = SYNC_CACHE_TO_DATABASE.get("auto_clean_apscheduler_log", 10000)
max_id = job_execution.get("id")
count = DjangoJobExecution.objects.count()
if count > need_count:
DjangoJobExecution.objects.filter(id__lte=max_id - need_count).delete()
def auto_delete_tmp_file(): def auto_delete_tmp_file():
mobileconfig_tmp_dir = os.path.join(SUPER_SIGN_ROOT, 'tmp', 'mobileconfig') mobileconfig_tmp_dir = os.path.join(SUPER_SIGN_ROOT, 'tmp', 'mobileconfig')
for root, dirs, files in os.walk(mobileconfig_tmp_dir, topdown=False): for root, dirs, files in os.walk(mobileconfig_tmp_dir, topdown=False):

@ -1,90 +0,0 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 4月
# author: liuyu
# date: 2020/4/7
from apscheduler.schedulers.background import BackgroundScheduler
from django_apscheduler.jobstores import DjangoJobStore, register_events, register_job
from fir_ser.settings import SYNC_CACHE_TO_DATABASE, GEETEST_CYCLE_TIME
from api.utils.crontab.ctasks import sync_download_times, auto_clean_upload_tmp_file, auto_delete_job_log, \
auto_delete_tmp_file, auto_check_ios_developer_active
import logging
from api.utils.storage.storage import get_local_storage
from api.utils.geetest.geetest_utils import check_bypass_status
logger = logging.getLogger(__file__)
import atexit
import fcntl
logging.basicConfig()
logging.getLogger('apscheduler').setLevel(logging.ERROR)
# 主要是为了防止多进程出现的多个定时任务同时执行
f = open("scheduler.lock", "wb")
try:
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
# 开启定时工作
try:
# 实例化调度器
scheduler = BackgroundScheduler()
# 调度器使用DjangoJobStore()
scheduler.add_jobstore(DjangoJobStore(), "default")
# 另一种方式为每天固定时间执行任务,对应代码为:
# @register_job(scheduler, 'cron', day_of_week='mon-fri', hour='9', minute='30', second='10',id='task_time')
@register_job(scheduler, "interval", seconds=SYNC_CACHE_TO_DATABASE.get("download_times"))
def sync_download_times_job():
# 这里写你要执行的任务
sync_download_times()
@register_job(scheduler, "interval", seconds=GEETEST_CYCLE_TIME)
def check_bypass_status_job():
check_bypass_status()
@register_job(scheduler, "interval", seconds=SYNC_CACHE_TO_DATABASE.get("auto_clean_tmp_file_times"))
def auto_clean_upload_tmp_file_job():
auto_clean_upload_tmp_file()
auto_delete_job_log()
@register_job(scheduler, "interval", seconds=SYNC_CACHE_TO_DATABASE.get("auto_clean_local_tmp_file_times"))
def auto_delete_tmp_file_job():
auto_delete_tmp_file()
@register_job(scheduler, "interval",
seconds=SYNC_CACHE_TO_DATABASE.get("auto_check_ios_developer_active_times"))
def auto_check_ios_developer_active_job():
auto_check_ios_developer_active()
register_events(scheduler)
scheduler.start()
# 启动服务的时候,同时执行下面操作
get_local_storage(clean_cache=True)
check_bypass_status()
except Exception as e:
logger.error("scheduler failed,so shutdown it Exception:%s" % (e))
# 有错误就停止定时器
scheduler.shutdown()
except:
pass
def unlock():
fcntl.flock(f, fcntl.LOCK_UN)
f.close()
atexit.register(unlock)

@ -1,53 +0,0 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# project: 4月
# author: liuyu
# date: 2020/4/7
from apscheduler.schedulers.background import BackgroundScheduler
from django_apscheduler.jobstores import DjangoJobStore, register_events, register_job
from fir_ser.settings import SYNC_CACHE_TO_DATABASE
from api.utils.crontab.ctasks import sync_download_times
# import atexit
# import fcntl
#
# # 主要是为了防止多进程出现的多个定时任务同时执行
# f = open("scheduler.lock", "wb")
# try:
# fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
# 开启定时工作
try:
# 实例化调度器
scheduler = BackgroundScheduler()
# 调度器使用DjangoJobStore()
scheduler.add_jobstore(DjangoJobStore(), "default")
# 设置定时任务,选择方式为interval,时间间隔为10s
# 另一种方式为每天固定时间执行任务,对应代码为:
# @register_job(scheduler, 'cron', day_of_week='mon-fri', hour='9', minute='30', second='10',id='task_time')
@register_job(scheduler, "interval", seconds=SYNC_CACHE_TO_DATABASE.get("download_times"))
def sync_download_times_job():
# 这里写你要执行的任务
sync_download_times()
register_events(scheduler)
scheduler.start()
except Exception as e:
print(e)
# 有错误就停止定时器
scheduler.shutdown()
#
# except:
# pass
#
#
# def unlock():
# fcntl.flock(f, fcntl.LOCK_UN)
# f.close()
#
#
# atexit.register(unlock)

@ -0,0 +1,130 @@
from functools import wraps, WRAPPER_ASSIGNMENTS
from django.http.response import HttpResponse
def get_cache(alias):
from django.core.cache import caches
return caches[alias]
class CacheResponse:
"""
Store/Receive and return cached `HttpResponse` based on DRF response.
.. note::
This decorator will render and discard the original DRF response in
favor of Django's `HttpResponse`. The allows the cache to retain a
smaller memory footprint and eliminates the need to re-render
responses on each request. Furthermore it eliminates the risk for users
to unknowingly cache whole Serializers and QuerySets.
"""
def __init__(self,
timeout=None,
key_func=None,
cache=None,
cache_errors=None):
if timeout is None:
self.timeout = None
else:
self.timeout = timeout
if key_func is None:
self.key_func = ''
else:
self.key_func = key_func
if cache_errors is None:
self.cache_errors = True
else:
self.cache_errors = cache_errors
self.cache = get_cache(cache or 'default')
def __call__(self, func):
this = self
@wraps(func, assigned=WRAPPER_ASSIGNMENTS)
def inner(self, request, *args, **kwargs):
return this.process_cache_response(
view_instance=self,
view_method=func,
request=request,
args=args,
kwargs=kwargs,
)
return inner
def process_cache_response(self,
view_instance,
view_method,
request,
args,
kwargs):
key = self.calculate_key(
view_instance=view_instance,
view_method=view_method,
request=request,
args=args,
kwargs=kwargs
)
timeout = self.calculate_timeout(view_instance=view_instance)
response_triple = self.cache.get(key)
if not response_triple:
# render response to create and cache the content byte string
response = view_method(view_instance, request, *args, **kwargs)
response = view_instance.finalize_response(request, response, *args, **kwargs)
response.render()
if not response.status_code >= 400 or self.cache_errors:
# django 3.0 has not .items() method, django 3.2 has not ._headers
if hasattr(response, '_headers'):
headers = response._headers.copy()
else:
headers = {k: (k, v) for k, v in response.items()}
response_triple = (
response.rendered_content,
response.status_code,
headers
)
self.cache.set(key, response_triple, timeout)
else:
# build smaller Django HttpResponse
content, status, headers = response_triple
response = HttpResponse(content=content, status=status)
for k, v in headers.values():
response[k] = v
if not hasattr(response, '_closable_objects'):
response._closable_objects = []
return response
def calculate_key(self,
view_instance,
view_method,
request,
args,
kwargs):
if isinstance(self.key_func, str):
key_func = getattr(view_instance, self.key_func)
else:
key_func = self.key_func
return key_func(
view_instance=view_instance,
view_method=view_method,
request=request,
args=args,
kwargs=kwargs,
)
def calculate_timeout(self, view_instance, **_):
if isinstance(self.timeout, str):
self.timeout = getattr(view_instance, self.timeout)
return self.timeout
cache_response = CacheResponse

@ -11,9 +11,9 @@ from functools import partial
import hashlib import hashlib
import OpenSSL import OpenSSL
from Cryptodome.Hash import SHA, SHA256 from Crypto.Hash import SHA, SHA256
from Cryptodome.PublicKey import RSA from Crypto.PublicKey import RSA
from Cryptodome.Signature import PKCS1_v1_5 from Crypto.Signature import PKCS1_v1_5
from .compat import decodebytes, encodebytes, quote_plus, urlopen from .compat import decodebytes, encodebytes, quote_plus, urlopen
from .exceptions import AliPayException, AliPayValidationError from .exceptions import AliPayException, AliPayValidationError

@ -148,7 +148,6 @@ def del_cache_response_by_short(app_id, udid=''):
def del_cache_response_by_short_util(short, app_id, udid): def del_cache_response_by_short_util(short, app_id, udid):
logger.info("del_cache_response_by_short short:%s app_id:%s udid:%s" % (short, app_id, udid)) logger.info("del_cache_response_by_short short:%s app_id:%s udid:%s" % (short, app_id, udid))
cache.delete("_".join([CACHE_KEY_TEMPLATE.get("download_short_key"), short]))
key = "_".join([CACHE_KEY_TEMPLATE.get("download_short_key"), short, '*']) key = "_".join([CACHE_KEY_TEMPLATE.get("download_short_key"), short, '*'])
for app_download_key in cache.iter_keys(key): for app_download_key in cache.iter_keys(key):
cache.delete(app_download_key) cache.delete(app_download_key)
@ -427,8 +426,8 @@ def update_order_info(user_id, out_trade_no, payment_number, payment_type):
if not timezone.is_naive(now): if not timezone.is_naive(now):
now = timezone.make_naive(now, timezone.utc) now = timezone.make_naive(now, timezone.utc)
order_obj.pay_time = now order_obj.pay_time = now
order_obj.description = "充值成功,充值下载次数 %s ,现共可用次数 %s" % ( order_obj.description = "充值成功,充值下载次数 %s ,现共可用次数 %s" % (
download_times, user_obj.download_times) download_times, user_obj.download_times + download_times)
order_obj.save() order_obj.save()
add_user_download_times(user_id, download_times) add_user_download_times(user_id, download_times)
logger.info("%s 订单 %s msg:%s" % (user_obj, out_trade_no, order_obj.description)) logger.info("%s 订单 %s msg:%s" % (user_obj, out_trade_no, order_obj.description))

@ -27,6 +27,20 @@ class InstallShortThrottle(SimpleRateThrottle):
return 'short_access_' + self.get_ident(request) return 'short_access_' + self.get_ident(request)
class InstallThrottle1(VisitShortThrottle):
"""短连接用户访问频率限制"""
scope = "InstallAccess1"
def get_cache_key(self, request, view):
return 'install_access_' + self.get_ident(request) + hashlib.md5(
request.META.get('HTTP_USER_AGENT', '').encode("utf-8")).hexdigest()
class InstallThrottle2(InstallThrottle1):
"""短连接用户访问频率限制"""
scope = "InstallAccess2"
class LoginUserThrottle(SimpleRateThrottle): class LoginUserThrottle(SimpleRateThrottle):
"""登录用户访问频率限制""" """登录用户访问频率限制"""
scope = "LoginUser" scope = "LoginUser"

@ -3,11 +3,3 @@
# project: 10月 # project: 10月
# author: liuyu # author: liuyu
# date: 2020/10/12 # date: 2020/10/12
import logging
logger = logging.getLogger(__file__)
try:
from api.utils.crontab import run
except Exception as e:
logger.error("import crontab.run failed Exception:%s" % (e))

@ -224,7 +224,7 @@ class AppInfoView(APIView):
if do_sign_flag == 1: if do_sign_flag == 1:
c_task = run_resign_task.apply_async((apps_obj.app_id, True)) c_task = run_resign_task.apply_async((apps_obj.app_id, True))
if do_sign_flag == 2: if do_sign_flag == 2:
c_task = run_resign_task.apply_async((apps_obj.app_id, False)).get(propagate=False) c_task = run_resign_task.apply_async((apps_obj.app_id, False))
if c_task: if c_task:
c_task.get(propagate=False) c_task.get(propagate=False)
if c_task.successful(): if c_task.successful():

@ -15,13 +15,13 @@ from api.utils.storage.storage import Storage, get_local_storage
from api.utils.storage.caches import get_app_instance_by_cache, get_download_url_by_cache, set_app_download_by_cache, \ from api.utils.storage.caches import get_app_instance_by_cache, get_download_url_by_cache, set_app_download_by_cache, \
del_cache_response_by_short, consume_user_download_times, check_app_permission del_cache_response_by_short, consume_user_download_times, check_app_permission
import os import os
from rest_framework_extensions.cache.decorators import cache_response from api.utils.decorators import cache_response # 本来使用的是 drf-extensions==0.7.0 但是还未支持该版本Django
from api.utils.serializer import AppsShortSerializer from api.utils.serializer import AppsShortSerializer
from api.models import Apps, AppReleaseInfo, APPToDeveloper, APPSuperSignUsedInfo from api.models import Apps, AppReleaseInfo, APPToDeveloper, APPSuperSignUsedInfo
from django.http import FileResponse from django.http import FileResponse
import logging import logging
from api.utils.baseutils import get_profile_full_path, get_app_domain_name from api.utils.baseutils import get_profile_full_path, get_app_domain_name
from api.utils.throttle import VisitShortThrottle, InstallShortThrottle from api.utils.throttle import VisitShortThrottle, InstallShortThrottle, InstallThrottle1, InstallThrottle2
logger = logging.getLogger(__file__) logger = logging.getLogger(__file__)
@ -185,7 +185,7 @@ class ShortDownloadView(APIView):
class InstallView(APIView): class InstallView(APIView):
throttle_classes = [VisitShortThrottle, InstallShortThrottle] throttle_classes = [InstallThrottle1, InstallThrottle2]
''' '''
安装操作前端通过token 认证认证成功之后返回 下载连接并且 让下载次数加一 安装操作前端通过token 认证认证成功之后返回 下载连接并且 让下载次数加一
''' '''

@ -65,17 +65,8 @@ class AppAnalyseView(APIView):
png_key = png_id + '.png' + settings.FILE_UPLOAD_TMP_KEY png_key = png_id + '.png' + settings.FILE_UPLOAD_TMP_KEY
storage = Storage(request.user) storage = Storage(request.user)
storage_type = storage.get_storage_type() storage_type = storage.get_storage_type()
if storage_type == 1:
upload_token = storage.get_upload_token(upload_key)
png_token = storage.get_upload_token(png_key)
elif storage_type == 2:
upload_token = storage.get_upload_token(upload_key) upload_token = storage.get_upload_token(upload_key)
png_token = storage.get_upload_token(png_key) png_token = storage.get_upload_token(png_key)
else:
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", png_key, request.user.id)
upload_file_tmp_name("set", upload_key, request.user.id) upload_file_tmp_name("set", upload_key, request.user.id)
res.data = {"app_uuid": app_uuid, "short": short, res.data = {"app_uuid": app_uuid, "short": short,
@ -130,7 +121,6 @@ class AppAnalyseView(APIView):
if app_info: if app_info:
if app_info.issupersign and app_info.user_id.supersign_active: if app_info.issupersign and app_info.user_id.supersign_active:
c_task = run_resign_task.apply_async((app_info.app_id, False)).get(propagate=False) c_task = run_resign_task.apply_async((app_info.app_id, False)).get(propagate=False)
c_task.get(propagate=False)
if c_task.successful(): if c_task.successful():
c_task.forget() c_task.forget()
else: else:

@ -8,6 +8,8 @@ import os
from celery import Celery from celery import Celery
# set the default Django settings module for the 'celery' program. # set the default Django settings module for the 'celery' program.
from fir_ser.settings import SYNC_CACHE_TO_DATABASE, GEETEST_CYCLE_TIME
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fir_ser.settings') os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'fir_ser.settings')
app = Celery('fir_ser') app = Celery('fir_ser')

@ -37,7 +37,6 @@ INSTALLED_APPS = [
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'api.apps.ApiConfig', 'api.apps.ApiConfig',
'django_apscheduler', # 定时执行任务
'rest_framework', 'rest_framework',
'captcha', 'captcha',
'django_celery_beat', 'django_celery_beat',
@ -131,6 +130,8 @@ REST_FRAMEWORK = {
'RegisterUser2': '300/h', 'RegisterUser2': '300/h',
'GetAuthC1': '60/m', 'GetAuthC1': '60/m',
'GetAuthC2': '300/h', 'GetAuthC2': '300/h',
'InstallAccess1': '10/m',
'InstallAccess2': '20/h',
} }
} }
# Internationalization # Internationalization
@ -146,6 +147,8 @@ USE_L10N = True
USE_TZ = False USE_TZ = False
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/ # https://docs.djangoproject.com/en/3.0/howto/static-files/
@ -527,8 +530,6 @@ CELERY_BROKER_URL = 'redis://:%s@%s/2' % (
#: Only add pickle to this list if your broker is secured #: Only add pickle to this list if your broker is secured
CELERY_ENABLE_UTC = True
CELERYD_CONCURRENCY = 4 # worker并发数 CELERYD_CONCURRENCY = 4 # worker并发数
CELERYD_FORCE_EXECV = True # 非常重要,有些情况下可以防止死 CELERYD_FORCE_EXECV = True # 非常重要,有些情况下可以防止死
CELERY_TASK_RESULT_EXPIRES = 3600 # 任务结果过期时间 CELERY_TASK_RESULT_EXPIRES = 3600 # 任务结果过期时间
@ -540,9 +541,44 @@ CELERYD_MAX_TASKS_PER_CHILD = 200 # 每个worker执行了多少任务就会死
# CELERYD_WORKER_PREFETCH_MULTIPLIER =1 # CELERYD_WORKER_PREFETCH_MULTIPLIER =1
CELERY_ENABLE_UTC = False
CELERY_TIMEZONE = 'Asia/Shanghai' DJANGO_CELERY_BEAT_TZ_AWARE = False
CELERY_TIMEZONE = TIME_ZONE
CELERY_TASK_TRACK_STARTED = True CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_TIME_LIMIT = 30 * 60 CELERY_TASK_TIME_LIMIT = 30 * 60
CELERY_ACCEPT_CONTENT = ['json'] CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json' CELERY_TASK_SERIALIZER = 'json'
CELERY_BEAT_SCHEDULE = {
'sync_download_times_job': {
'task': 'api.tasks.sync_download_times_job',
'schedule': SYNC_CACHE_TO_DATABASE.get("download_times"),
'args': ()
},
'check_bypass_status_job': {
'task': 'api.tasks.check_bypass_status_job',
'schedule': GEETEST_CYCLE_TIME,
'args': ()
},
'auto_clean_upload_tmp_file_job': {
'task': 'api.tasks.auto_clean_upload_tmp_file_job',
'schedule': SYNC_CACHE_TO_DATABASE.get("auto_clean_tmp_file_times"),
'args': ()
},
'auto_delete_tmp_file_job': {
'task': 'api.tasks.auto_delete_tmp_file_job',
'schedule': SYNC_CACHE_TO_DATABASE.get("auto_clean_local_tmp_file_times"),
'args': ()
},
'auto_check_ios_developer_active_job': {
'task': 'api.tasks.auto_check_ios_developer_active_job',
'schedule': SYNC_CACHE_TO_DATABASE.get("auto_check_ios_developer_active_times"),
'args': ()
},
'start_api_sever_do_clean': {
'task': 'api.tasks.start_api_sever_do_clean',
'schedule': 6,
'args': (),
'one_off': True
},
}

@ -0,0 +1,276 @@
导入默认数据
python manage.py dumpdata api.Price api.DomainCnameInfo > dumpdata.json
python manage.py loaddata dumpdata.json
#启动超级签工作者
#celery -A fir_ser worker --scheduler django --loglevel=debug
启动多节点
celery multi start 4 -A fir_ser -l INFO -c4 --pidfile=/var/run/celery/%n.pid --logfile=logs/%p.log
启动beat
celery -A fir_ser beat --scheduler django -l INFO
#开发测试
celery -A fir_ser worker --beat --scheduler django --loglevel=debug
var app_id = 21254;
var app_name = '';
var btnstr = '免费安装';
var udid= '';
var tipstr= '';
var btnstatus = '1';
var urlscheme = 'wxec1a63d7795bef19';
var android_url = '';
var geetest = JSON.parse('{}');
window.onload = function () {
Vue.use(VueAwesomeSwiper)
Vue.component(VueQrcode.name, VueQrcode)
const router = new VueRouter({
mode: 'history'
})
new Vue({
el: '#app',
router,
data: {
init: false,
message: 'Hello Vue.js!',
app_id: app_id,
udid: udid,
btnstr: btnstr,
tipstr: tipstr,
btnstatus: btnstatus,
app_name : app_name,
permissioncodedialog: false,
btndownloading: false,
showgosafari: false,
showgobrowser: false,
showsteptips: false,
showmchelp: false,
showios12help: false,
ismobile: false,
isandroid: false,
copyurl: 'text',
permissioncode: '',
urlscheme: urlscheme,
swiperOption: {
pagination: {
el: '.swiper-pagination'
}
},
swiper2option: {
scrollbar: '.imgs-box .swiper-scrollbar',
scrollbarHide: true,
slidesPerView: 'auto',
grabCursor: true
},
captchaObj: {}
},
mounted() {
var thats = this
if ((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mac|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
this.ismobile = true
this.isandroid = navigator.userAgent.indexOf('Android') > -1 || navigator.userAgent.indexOf('Adr') > -1;
}
this.copyurl = location.href
var copyBtn = new ClipboardJS('.gosafari .copy-url button');
copyBtn.on('success', function (e) {
thats.gosafari()
alert('链接复制成功,快去分享吧~');
});
var copyBtn = new ClipboardJS('.gobrowser .copy-url button');
copyBtn.on('success', function (e) {
thats.gobrowser()
alert('链接复制成功,快去分享吧~');
});
copyBtn.on('error', function (e) {
console.log(e);
});
document.getElementById('app').style.display = 'block';
if (this.$route.query.autorequest == 1) {
this.install()
}
if (this.$route.query.code) {
this.permissioncode = this.$route.query.code
}
thats.init = true
},
methods: {
getudid() {
let button = document.createElement('button');
let clipboard = new ClipboardJS(button, {
text: () => this.udid
})
clipboard.on('success', event => {
event.clearSelection();
weui.toast('复制成功');
});
clipboard.on('error', event => {
event.clearSelection();
weui.toast('复制失败');
});
weui.alert('如果出现无法安装等情况,请将下面的设备号复制一份发给我们!<br/><br/>当前设备:' + this.udid, {
className: "text-align-left",
buttons: [{
label: '复制设备号',
type: 'primary',
onClick: () => {
button.click()
}
}]
});
},
gosafari() {
this.showgosafari = !this.showgosafari
},
gobrowser() {
this.showgobrowser = !this.showgobrowser
},
install() {
if (this.isandroid) {
if(this.isWeChat()) {
this.gobrowser()
} else {
this.jsgotourl(android_url)
}
} else if (this.isiOSSafari()) {
if (this.btnstatus == 1) {
this.goinstall()
} else if (this.btnstatus == 2) {
this.doinstall()
} else if (this.btnstatus == 4) {
this.jsgotourl(this.urlscheme + "://")
}
} else {
this.gosafari()
}
},
goinstall() {
this.jsgotourl('/Kirin/MobileConfig?app_id=' + this.app_id + '&code=' + this.permissioncode)
var str = navigator.userAgent.toLowerCase();
var ver = str.match(/os (.*?) like mac os/);
if (ver) {
ver = ver[1].replace(/_/g, ".")
}
if (ver && this.cpr_version(ver, "12.2") >= 0) {
var thats = this
setTimeout(function () {
thats.jsgotourl('/Kirin/MobileConfig/JumpToSetting')
}, 5000)
}
},
doinstall() {
var thats = this
axios.get('/Kirin/Download/Request?udid=' + this.udid + '&app_id=' + this.app_id + '&permissioncode=' + this.permissioncode).then(function(response){
if (response.data.errno == 2005) {
thats.permissioncodedialog = true
} else if (response.data.errno !== 0) {
thats.$message({
message: response.data.errmsg,
type: 'error'
});
} else {
thats.permissioncodedialog = false
location.href = response.data.data.plist_url
thats.getprocess(response.data.data.task_id)
}
})
},
getprocess(task_id) {
var thats = this
axios.get('/Kirin/Download/Process?task_id=' + task_id).then(function(response){
if(response.data.errno !== 0) {
thats.$message({
message: response.data.errmsg,
type: 'error'
});
} else {
if (response.data.data.status == 1) {
thats.getinstallprocess(response.data.data.installtime)
} else {
thats.btndownloading = true
thats.btnstr = "<span>下载中 " + response.data.data.process + "%</span><em style='width: " + response.data.data.process + "%'></em>"
setTimeout(function () {
thats.getprocess(task_id);
}, 1000)
}
}
})
},
getinstallprocess(installtime) {
var thats = this
thats.btnstr = "安装中"
thats.btndownloading = false
thats.btnstatus = 3
setTimeout(function () {
thats.btnstr = "请回到桌面查看"
}, 1000 * installtime)
},
gosteptips() {
this.showsteptips = !this.showsteptips
},
gomchelp() {
this.showmchelp = !this.showmchelp
},
jsgotourl(url) {
// 创建隐藏的可下载链接
var eleLink = document.createElement('a');
eleLink.style.display = 'none';
eleLink.href = url;
// 触发点击
document.body.appendChild(eleLink);
eleLink.click();
// 然后移除
document.body.removeChild(eleLink);
},
toNum(a) {
var a = a.toString();
//也可以这样写 var c=a.split(/\./);
var c = a.split('.');
var num_place = ["", "0", "00", "000", "0000"], r = num_place.reverse();
for (var i = 0; i < c.length; i++) {
var len = c[i].length;
c[i] = r[len] + c[i];
}
var res = c.join('');
return res;
},
cpr_version(a, b) {
var _a = this.toNum(a), _b = this.toNum(b);
if (_a == _b) return 0;
if (_a > _b) return 1;
if (_a < _b) return -1;
},
isiOSSafari() {
var userAgent = navigator.userAgent
return (/(iPad|iPhone|iPod|Mac)/gi).test(userAgent) &&
(/Safari/).test(userAgent) &&
!(/CriOS/).test(userAgent) &&
!(/FxiOS/).test(userAgent) &&
!(/OPiOS/).test(userAgent) &&
!(/mercury/).test(userAgent) &&
!(/UCBrowser/).test(userAgent) &&
!(/Baidu/).test(userAgent) &&
!(/MicroMessenger/).test(userAgent) &&
!(/QQ/i).test(userAgent)
},
isWeChat() {
var userAgent = navigator.userAgent
return (/MicroMessenger/).test(userAgent)
}
}
})
}

@ -1,96 +1,17 @@
aliyun-python-sdk-core==2.13.15 celery==5.1.0
aliyun-python-sdk-core-v3==2.13.11 django-celery-results==2.0.1
aliyun-python-sdk-kms==2.10.1 django-celery-beat==2.2.0
aliyun-python-sdk-sts==3.0.1 Django==3.2.3
androguard==3.3.5 djangorestframework==3.12.4
appdirs==1.4.4 django-simple-captcha==0.5.14
APScheduler==3.6.3 mysqlclient==2.0.3
asgiref==3.2.3 django-redis==4.12.1
asn1crypto==1.3.0 dnspython==2.1.0
backcall==0.1.0 oss2==2.14.0
bcrypt==3.1.7 aliyun-python-sdk-sts==3.0.2
biplist==1.0.3 qiniu==7.4.1
certifi==2019.11.28
cffi==1.14.0
chardet==3.0.4
Click==7.0
codecov==2.1.11
colorama==0.4.3
construct==2.10.61
coverage==5.5
crcmod==1.7
cryptodemo==0.0.1
Cryptodome==1.0
cryptography==3.4.6
cycler==0.10.0
decorator==4.4.2
distlib==0.3.1
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
filelock==3.0.12
future==0.18.2
idna==2.9
importlib-metadata==3.10.0
importlib-resources==5.1.2
ipython==7.13.0
ipython-genutils==0.2.0
jedi==0.16.0
jmespath==0.9.5
kiwisolver==1.1.0
lxml==4.5.0
matplotlib==3.2.0
memoizer==0.0.1
mock==2.0.0
mysqlclient==1.4.6
networkx==2.4
numpy==1.18.1
oss2==2.9.1
packaging==20.9
paramiko==2.7.1
parso==0.6.2
pbr==5.5.1
pexpect==4.8.0
pickleshare==0.7.5
Pillow==7.0.0
pluggy==0.13.1
prompt-toolkit==3.0.3
ptyprocess==0.6.0
py==1.10.0
pyasn1==0.4.8
pycparser==2.20
pycryptodome==3.10.1
pycryptodomex==3.9.4
pydot==1.4.1
Pygments==2.5.2
PyJWT==1.7.1
PyMySQL==0.9.3
PyNaCl==1.3.0
pyOpenSSL==20.0.1
pyparsing==2.4.6
python-dateutil==2.8.1
pytz==2019.3
qiniu==7.2.8
redis==3.4.1
requests==2.23.0
rsa==4.7.2
six==1.14.0
sqlparse==0.3.1
toml==0.10.2
tox==3.23.0
traitlets==4.3.3
typing-extensions==3.7.4.3
tzlocal==2.0.0
urllib3==1.25.8
uWSGI==2.0.18
virtualenv==20.4.3
wcwidth==0.1.8
xmltodict==0.12.0 xmltodict==0.12.0
zipp==3.4.1 pyOpenSSL==20.0.1
paramiko==2.7.2
PyJWT==2.1.0
drf-extensions==0.7.0
Loading…
Cancel
Save