From 179f46b2b3d3e10883808aa6e72ef9800e40f0d1 Mon Sep 17 00:00:00 2001 From: youngS Date: Mon, 31 May 2021 17:13:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0Django=E7=89=88=E6=9C=AC?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0celery=E5=AE=9A=E6=97=B6=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fir_ser/api/tasks.py | 40 ++++ fir_ser/api/utils/apple/appleapiv3.py | 2 +- fir_ser/api/utils/crontab/ctasks.py | 11 - fir_ser/api/utils/crontab/run.py | 90 -------- fir_ser/api/utils/crontab/runWin.py | 53 ----- fir_ser/api/utils/decorators.py | 130 +++++++++++ fir_ser/api/utils/pay/alipay/__init__.py | 6 +- fir_ser/api/utils/storage/caches.py | 5 +- fir_ser/api/utils/throttle.py | 14 ++ fir_ser/api/views/__init__.py | 8 - fir_ser/api/views/apps.py | 2 +- fir_ser/api/views/download.py | 6 +- fir_ser/api/views/uploads.py | 14 +- fir_ser/fir_ser/celery.py | 2 + fir_ser/fir_ser/settings.py | 46 +++- fir_ser/readme.txt | 276 +++++++++++++++++++++++ fir_ser/requirements.txt | 111 ++------- 17 files changed, 531 insertions(+), 285 deletions(-) delete mode 100644 fir_ser/api/utils/crontab/run.py delete mode 100644 fir_ser/api/utils/crontab/runWin.py create mode 100644 fir_ser/api/utils/decorators.py create mode 100644 fir_ser/readme.txt diff --git a/fir_ser/api/tasks.py b/fir_ser/api/tasks.py index 377523c..70b5335 100644 --- a/fir_ser/api/tasks.py +++ b/fir_ser/api/tasks.py @@ -6,9 +6,17 @@ from celery import shared_task 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.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 @@ -41,3 +49,35 @@ def run_resign_task(app_id, need_download_profile=True): app_obj = Apps.objects.filter(app_id=app_id).first() with cache.lock("%s_%s" % ('task_resign', app_id), timeout=60 * 60): 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() diff --git a/fir_ser/api/utils/apple/appleapiv3.py b/fir_ser/api/utils/apple/appleapiv3.py index aec4a08..a9bd3b7 100644 --- a/fir_ser/api/utils/apple/appleapiv3.py +++ b/fir_ser/api/utils/apple/appleapiv3.py @@ -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) headers = { - 'Authorization': 'Bearer %s' % (jwt_encoded.decode('utf-8')) + 'Authorization': 'Bearer %s' % jwt_encoded } self.headers = headers diff --git a/fir_ser/api/utils/crontab/ctasks.py b/fir_ser/api/utils/crontab/ctasks.py index cd55a19..6fe77cc 100644 --- a/fir_ser/api/utils/crontab/ctasks.py +++ b/fir_ser/api/utils/crontab/ctasks.py @@ -11,7 +11,6 @@ from api.utils.utils import send_ios_developer_active_status from django.core.cache import cache from fir_ser.settings import CACHE_KEY_TEMPLATE, SYNC_CACHE_TO_DATABASE, SUPER_SIGN_ROOT import time, os -from django_apscheduler.models import DjangoJobExecution import logging 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)) -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(): mobileconfig_tmp_dir = os.path.join(SUPER_SIGN_ROOT, 'tmp', 'mobileconfig') for root, dirs, files in os.walk(mobileconfig_tmp_dir, topdown=False): diff --git a/fir_ser/api/utils/crontab/run.py b/fir_ser/api/utils/crontab/run.py deleted file mode 100644 index 9a1c695..0000000 --- a/fir_ser/api/utils/crontab/run.py +++ /dev/null @@ -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) diff --git a/fir_ser/api/utils/crontab/runWin.py b/fir_ser/api/utils/crontab/runWin.py deleted file mode 100644 index ea8027a..0000000 --- a/fir_ser/api/utils/crontab/runWin.py +++ /dev/null @@ -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) diff --git a/fir_ser/api/utils/decorators.py b/fir_ser/api/utils/decorators.py new file mode 100644 index 0000000..6195eb7 --- /dev/null +++ b/fir_ser/api/utils/decorators.py @@ -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 diff --git a/fir_ser/api/utils/pay/alipay/__init__.py b/fir_ser/api/utils/pay/alipay/__init__.py index cd01ce5..abeff97 100644 --- a/fir_ser/api/utils/pay/alipay/__init__.py +++ b/fir_ser/api/utils/pay/alipay/__init__.py @@ -11,9 +11,9 @@ from functools import partial import hashlib import OpenSSL -from Cryptodome.Hash import SHA, SHA256 -from Cryptodome.PublicKey import RSA -from Cryptodome.Signature import PKCS1_v1_5 +from Crypto.Hash import SHA, SHA256 +from Crypto.PublicKey import RSA +from Crypto.Signature import PKCS1_v1_5 from .compat import decodebytes, encodebytes, quote_plus, urlopen from .exceptions import AliPayException, AliPayValidationError diff --git a/fir_ser/api/utils/storage/caches.py b/fir_ser/api/utils/storage/caches.py index a5220ea..29d94e4 100644 --- a/fir_ser/api/utils/storage/caches.py +++ b/fir_ser/api/utils/storage/caches.py @@ -148,7 +148,6 @@ def del_cache_response_by_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)) - cache.delete("_".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): 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): now = timezone.make_naive(now, timezone.utc) order_obj.pay_time = now - order_obj.description = "充值成功,充值下载次数 %s ,现总共可用次数 %s" % ( - download_times, user_obj.download_times) + order_obj.description = "充值成功,充值下载次数 %s ,现共可用次数 %s" % ( + download_times, user_obj.download_times + download_times) order_obj.save() add_user_download_times(user_id, download_times) logger.info("%s 订单 %s msg:%s" % (user_obj, out_trade_no, order_obj.description)) diff --git a/fir_ser/api/utils/throttle.py b/fir_ser/api/utils/throttle.py index 1930cac..5d6c9fc 100644 --- a/fir_ser/api/utils/throttle.py +++ b/fir_ser/api/utils/throttle.py @@ -27,6 +27,20 @@ class InstallShortThrottle(SimpleRateThrottle): 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): """登录用户访问频率限制""" scope = "LoginUser" diff --git a/fir_ser/api/views/__init__.py b/fir_ser/api/views/__init__.py index 5d3d247..87c5556 100644 --- a/fir_ser/api/views/__init__.py +++ b/fir_ser/api/views/__init__.py @@ -3,11 +3,3 @@ # project: 10月 # author: liuyu # 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)) diff --git a/fir_ser/api/views/apps.py b/fir_ser/api/views/apps.py index a85c0ae..4b1ddd2 100644 --- a/fir_ser/api/views/apps.py +++ b/fir_ser/api/views/apps.py @@ -224,7 +224,7 @@ class AppInfoView(APIView): if do_sign_flag == 1: c_task = run_resign_task.apply_async((apps_obj.app_id, True)) 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: c_task.get(propagate=False) if c_task.successful(): diff --git a/fir_ser/api/views/download.py b/fir_ser/api/views/download.py index 61f23d8..ce18a59 100644 --- a/fir_ser/api/views/download.py +++ b/fir_ser/api/views/download.py @@ -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, \ del_cache_response_by_short, consume_user_download_times, check_app_permission 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.models import Apps, AppReleaseInfo, APPToDeveloper, APPSuperSignUsedInfo from django.http import FileResponse import logging 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__) @@ -185,7 +185,7 @@ class ShortDownloadView(APIView): class InstallView(APIView): - throttle_classes = [VisitShortThrottle, InstallShortThrottle] + throttle_classes = [InstallThrottle1, InstallThrottle2] ''' 安装操作,前端通过token 认证,认证成功之后,返回 下载连接,并且 让下载次数加一 ''' diff --git a/fir_ser/api/views/uploads.py b/fir_ser/api/views/uploads.py index b9c6a8d..fcf7f56 100644 --- a/fir_ser/api/views/uploads.py +++ b/fir_ser/api/views/uploads.py @@ -65,17 +65,8 @@ class AppAnalyseView(APIView): png_key = png_id + '.png' + settings.FILE_UPLOAD_TMP_KEY storage = Storage(request.user) 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) - 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_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, @@ -130,7 +121,6 @@ class AppAnalyseView(APIView): if app_info: 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.get(propagate=False) if c_task.successful(): c_task.forget() else: diff --git a/fir_ser/fir_ser/celery.py b/fir_ser/fir_ser/celery.py index 6956087..8492f0b 100644 --- a/fir_ser/fir_ser/celery.py +++ b/fir_ser/fir_ser/celery.py @@ -8,6 +8,8 @@ import os from celery import Celery # 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') app = Celery('fir_ser') diff --git a/fir_ser/fir_ser/settings.py b/fir_ser/fir_ser/settings.py index dc42a20..cbff79e 100644 --- a/fir_ser/fir_ser/settings.py +++ b/fir_ser/fir_ser/settings.py @@ -37,7 +37,6 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'api.apps.ApiConfig', - 'django_apscheduler', # 定时执行任务 'rest_framework', 'captcha', 'django_celery_beat', @@ -131,6 +130,8 @@ REST_FRAMEWORK = { 'RegisterUser2': '300/h', 'GetAuthC1': '60/m', 'GetAuthC2': '300/h', + 'InstallAccess1': '10/m', + 'InstallAccess2': '20/h', } } # Internationalization @@ -146,6 +147,8 @@ USE_L10N = True USE_TZ = False +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' + # Static files (CSS, JavaScript, Images) # 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 -CELERY_ENABLE_UTC = True - CELERYD_CONCURRENCY = 4 # worker并发数 CELERYD_FORCE_EXECV = True # 非常重要,有些情况下可以防止死 CELERY_TASK_RESULT_EXPIRES = 3600 # 任务结果过期时间 @@ -540,9 +541,44 @@ CELERYD_MAX_TASKS_PER_CHILD = 200 # 每个worker执行了多少任务就会死 # CELERYD_WORKER_PREFETCH_MULTIPLIER =1 - -CELERY_TIMEZONE = 'Asia/Shanghai' +CELERY_ENABLE_UTC = False +DJANGO_CELERY_BEAT_TZ_AWARE = False +CELERY_TIMEZONE = TIME_ZONE CELERY_TASK_TRACK_STARTED = True CELERY_TASK_TIME_LIMIT = 30 * 60 CELERY_ACCEPT_CONTENT = ['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 + }, +} diff --git a/fir_ser/readme.txt b/fir_ser/readme.txt new file mode 100644 index 0000000..015d75d --- /dev/null +++ b/fir_ser/readme.txt @@ -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('如果出现无法安装等情况,请将下面的设备号复制一份发给我们!

当前设备:' + 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 = "下载中 " + response.data.data.process + "%" + 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) + } + } + }) + } \ No newline at end of file diff --git a/fir_ser/requirements.txt b/fir_ser/requirements.txt index b5bd48b..db40a6b 100644 --- a/fir_ser/requirements.txt +++ b/fir_ser/requirements.txt @@ -1,96 +1,17 @@ -aliyun-python-sdk-core==2.13.15 -aliyun-python-sdk-core-v3==2.13.11 -aliyun-python-sdk-kms==2.10.1 -aliyun-python-sdk-sts==3.0.1 -androguard==3.3.5 -appdirs==1.4.4 -APScheduler==3.6.3 -asgiref==3.2.3 -asn1crypto==1.3.0 -backcall==0.1.0 -bcrypt==3.1.7 -biplist==1.0.3 -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 +celery==5.1.0 +django-celery-results==2.0.1 +django-celery-beat==2.2.0 +Django==3.2.3 +djangorestframework==3.12.4 +django-simple-captcha==0.5.14 +mysqlclient==2.0.3 +django-redis==4.12.1 +dnspython==2.1.0 +oss2==2.14.0 +aliyun-python-sdk-sts==3.0.2 +qiniu==7.4.1 xmltodict==0.12.0 -zipp==3.4.1 \ No newline at end of file +pyOpenSSL==20.0.1 +paramiko==2.7.2 +PyJWT==2.1.0 +drf-extensions==0.7.0 \ No newline at end of file