diff --git a/.gitignore b/.gitignore
index 4f53015..d560af3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,6 @@ fir_client/yarn.lock
fir_download/package-lock.json
fir_client/src/assets/down_left.png
fir_client/src/assets/down_right.png
+mailhtml/build/
+mailhtml/node_modules/
+mailhtml/.idea/
diff --git a/fir_ser/common/base/magic.py b/fir_ser/common/base/magic.py
index f2203bb..7cf145a 100644
--- a/fir_ser/common/base/magic.py
+++ b/fir_ser/common/base/magic.py
@@ -7,6 +7,7 @@ import datetime
import logging
import time
from functools import wraps
+from importlib import import_module
from django.core.cache import cache
@@ -115,3 +116,23 @@ def magic_notify(notify_rules, timeout=30 * 24 * 60 * 60):
else:
notify_cache.del_storage_cache()
+
+
+def import_from_string(dotted_path):
+ """
+ Import a dotted module path and return the attribute/class designated by the
+ last name in the path. Raise ImportError if the import failed.
+ """
+ try:
+ module_path, class_name = dotted_path.rsplit('.', 1)
+ except ValueError as err:
+ raise ImportError("%s doesn't look like a module path" % dotted_path) from err
+
+ module = import_module(module_path)
+
+ try:
+ return getattr(module, class_name)
+ except AttributeError as err:
+ raise ImportError('Module "%s" does not define a "%s" attribute/class' % (
+ module_path, class_name)
+ ) from err
diff --git a/fir_ser/common/core/sysconfig.py b/fir_ser/common/core/sysconfig.py
index 5dc71c6..fcf7340 100644
--- a/fir_ser/common/core/sysconfig.py
+++ b/fir_ser/common/core/sysconfig.py
@@ -14,7 +14,7 @@ from rest_framework import serializers
from api.models import SystemConfig
from common.cache.storage import SystemConfigCache
-from config import BASECONF, API_DOMAIN, MOBILEPROVISION, WEB_DOMAIN, THIRDLOGINCONF, AUTHCONF, IPACONF, MSGCONF, \
+from config import BASECONF, API_DOMAIN, MOBILEPROVISION, WEB_DOMAIN, THIRDLOGINCONF, AUTHCONF, IPACONF, \
DOWNLOADTIMESCONF, PAYCONF, STORAGEKEYCONF, SENDERCONF, APPLEDEVELOPERCONF
logger = logging.getLogger(__name__)
@@ -268,27 +268,6 @@ class UserDownloadTimesCache(ConfigCacheBase):
return super().get_value('AUTH_USER_GIVE_DOWNLOAD_TIMES', DOWNLOADTIMESCONF.AUTH_USER_GIVE_DOWNLOAD_TIMES)
-class EmailMsgCache(ConfigCacheBase):
- def __init__(self):
- super(EmailMsgCache, self).__init__()
-
- @property
- def MSG_NOT_EXIST_DEVELOPER(self):
- return super().get_value('MSG_NOT_EXIST_DEVELOPER', MSGCONF.MSG_NOT_EXIST_DEVELOPER)
-
- @property
- def MSG_SING_APP_OVER_LIMIT(self):
- return super().get_value('MSG_SING_APP_OVER_LIMIT', MSGCONF.MSG_SING_APP_OVER_LIMIT)
-
- @property
- def MSG_ERROR_DEVELOPER(self):
- return super().get_value('MSG_ERROR_DEVELOPER', MSGCONF.MSG_ERROR_DEVELOPER)
-
- @property
- def MSG_AUTO_CHECK_DEVELOPER(self):
- return super().get_value('MSG_AUTO_CHECK_DEVELOPER', MSGCONF.MSG_AUTO_CHECK_DEVELOPER)
-
-
class PayConfCache(ConfigCacheBase):
def __init__(self):
super(PayConfCache, self).__init__()
@@ -356,8 +335,8 @@ class AppleDeveloperConfCache(ConfigCacheBase):
return super().get_value('DEVELOPER_UID_KEY', APPLEDEVELOPERCONF.DEVELOPER_UID_KEY)
-class ConfigCache(BaseConfCache, IpaConfCache, AuthConfCache, EmailMsgCache, UserDownloadTimesCache, GeeTestConfCache,
- PayConfCache, ThirdPartConfCache, AppleDeveloperConfCache):
+class ConfigCache(BaseConfCache, IpaConfCache, AuthConfCache, UserDownloadTimesCache, GeeTestConfCache, PayConfCache,
+ ThirdPartConfCache, AppleDeveloperConfCache):
def __init__(self):
super(ConfigCache, self).__init__()
diff --git a/fir_ser/common/libs/sendmsg/emailApi.py b/fir_ser/common/libs/sendmsg/emailApi.py
index f1d8eb9..838e35d 100644
--- a/fir_ser/common/libs/sendmsg/emailApi.py
+++ b/fir_ser/common/libs/sendmsg/emailApi.py
@@ -7,6 +7,8 @@
from django.conf import settings
from django.core.mail import send_mail
+from common.base.magic import import_from_string
+
class EmailMsgSender(object):
def __init__(self, email_host, email_port, use_tls, use_ssl, subject, username, password, form, template_code):
@@ -33,7 +35,9 @@ class EmailMsgSender(object):
def send_msg(self, template_code, email, code):
try:
- response = send_mail(self.subject % {'code': ''}, template_code % {'code': code}, self.form, [email], )
+ html_content_obj = import_from_string(template_code)
+ content = html_content_obj(code)
+ response = send_mail(self.subject % {'code': ''}, content, self.form, [email], html_message=content)
if response == 1:
return True, 'OK'
else:
diff --git a/fir_ser/common/libs/sendmsg/template_content.py b/fir_ser/common/libs/sendmsg/template_content.py
new file mode 100644
index 0000000..f02bc53
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/template_content.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+# -*- coding:utf-8 -*-
+# project: fir_ser
+# filename: send_template_msg
+# author: liuyu
+# data: 2022/4/8
+
+from django.template import loader
+
+from common.base.baseutils import get_format_time
+
+
+def get_pay_success_html_content(user_obj, order_obj):
+ return loader.render_to_string('pay_success.html',
+ {
+ 'username': user_obj.first_name,
+ 'order_obj': order_obj,
+ })
+
+
+def get_sign_failed_html_content(user_obj, app_obj, developer_obj, now_time):
+ return loader.render_to_string('xsign/app_sign_failed.html',
+ {
+ 'username': user_obj.first_name,
+ 'app_obj': app_obj,
+ 'developer_obj': developer_obj,
+ 'now_time': now_time
+ })
+
+
+def get_sign_unavailable_developer_html_content(user_obj, app_obj, now_time):
+ return loader.render_to_string('xsign/apple_developer_unavailable.html',
+ {
+ 'username': user_obj.first_name,
+ 'app_obj': app_obj,
+ 'now_time': now_time
+ })
+
+
+def get_sign_app_over_limit_html_content(user_obj, app_obj, now_time, used_num, limit_number):
+ return loader.render_to_string('xsign/app_sign_over_limit.html',
+ {
+ 'username': user_obj.first_name,
+ 'app_obj': app_obj,
+ 'now_time': now_time,
+ 'used_num': used_num,
+ 'limit_number': limit_number,
+ })
+
+
+def get_check_developer_report_html_content(user_obj, developer_obj_list, developer_used_info, yesterday_used_number):
+ return loader.render_to_string('xsign/timing_task_notify.html',
+ {
+ 'username': user_obj.first_name,
+ 'developer_obj_list': developer_obj_list,
+ 'developer_used_info': developer_used_info,
+ 'yesterday_used_number': yesterday_used_number,
+ })
+
+
+def get_user_download_times_over_limit_html_content(user_obj):
+ return loader.render_to_string('download_times_over_limit.html',
+ {
+ 'username': user_obj.first_name,
+ 'user_obj': user_obj,
+ })
+
+
+def get_developer_devices_not_enough_html_content(user_obj, device_count):
+ return loader.render_to_string('xsign/apple_developer_devices_over_limit.html',
+ {
+ 'username': user_obj.first_name,
+ 'user_obj': user_obj,
+ 'device_count': device_count,
+ })
+
+
+def get_developer_cert_expired_html_content(user_obj, developer_obj_list):
+ return loader.render_to_string('xsign/apple_developer_cert_expired.html',
+ {
+ 'username': user_obj.first_name,
+ 'developer_obj_list': developer_obj_list,
+ })
+
+
+def get_user_download_times_not_enough_html_content(user_obj):
+ return loader.render_to_string('download_times_not_enough.html',
+ {
+ 'username': user_obj.first_name,
+ 'user_obj': user_obj,
+ })
+
+
+def get_userinfo_change_html_content(code):
+ return loader.render_to_string('userinfo/change_userinfo.html',
+ {
+ 'now_time': get_format_time().replace('_', ' '),
+ 'code': code,
+ })
+
+
+def get_userinfo_change_code_html_content(code):
+ return loader.render_to_string('userinfo/change_userinfo.html',
+ {
+ 'now_time': get_format_time().replace('_', ' '),
+ 'code': code,
+ })
+
+
+def get_code_notify_html_content(code):
+ return loader.render_to_string('userinfo/code_notify.html',
+ {
+ 'now_time': get_format_time().replace('_', ' '),
+ 'code': code,
+ })
+
+
+def get_userinfo_login_code_html_content(code):
+ return loader.render_to_string('userinfo/login_code.html',
+ {
+ 'now_time': get_format_time().replace('_', ' '),
+ 'code': code,
+ })
+
+
+def get_userinfo_register_code_html_content(code):
+ return loader.render_to_string('userinfo/register_code.html',
+ {
+ 'now_time': get_format_time().replace('_', ' '),
+ 'code': code,
+ })
+
+
+def get_userinfo_reset_pwd_html_content(code):
+ return loader.render_to_string('userinfo/reset_password.html',
+ {
+ 'now_time': get_format_time().replace('_', ' '),
+ 'code': code,
+ })
diff --git a/fir_ser/common/libs/sendmsg/templates/check_developer.html b/fir_ser/common/libs/sendmsg/templates/check_developer.html
deleted file mode 100644
index bb15fab..0000000
--- a/fir_ser/common/libs/sendmsg/templates/check_developer.html
+++ /dev/null
@@ -1,189 +0,0 @@
-
-
-
-
- 开发者状态监测报告
-
-
-
-
-
-
- 尊敬的用户 {{ username }} 你好,苹果开发者状态监测如下:
-
-
-
- 当前正常设备总量:{{ developer_used_info.used_sign_number }}
-
-
- 已使用:【平台:{{ developer_used_info.used_number }} 】【其他:{{ developer_used_info.can_other_used }}】
-
-
- 还剩:{{ developer_used_info.can_sign_number }} 可用
-
-
-
- 昨天消耗设备数:{{ yesterday_used_number }}
-
-
-
-
-
-
- 苹果开发者ID
- |
-
- 苹果开发者备注
- |
-
- 苹果开发者状态
- |
-
-
-
- {% for developer_obj in developer_obj_list %}
-
-
- {{ developer_obj.issuer_id }}
- |
-
- {{ developer_obj.description }}
- |
-
- {{ developer_obj.get_status_display }}
- |
-
-
- {% endfor %}
-
-
-
-
-
-
-
diff --git a/fir_ser/common/libs/sendmsg/templates/download_times_not_enough.html b/fir_ser/common/libs/sendmsg/templates/download_times_not_enough.html
new file mode 100644
index 0000000..82b64e9
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/download_times_not_enough.html
@@ -0,0 +1,291 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 账户下载余额不足通知
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 尊敬的用户{{ username }},您好:
+
+ |
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 您当前账户下载次数仅剩 {{ user_obj.download_times }}
+ |
+
+
+
+
+ 您当前账户下载次数不足,应用已经无法下载安装。为了避免业务使用,望您尽快充值
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/download_times_over_limit.html b/fir_ser/common/libs/sendmsg/templates/download_times_over_limit.html
new file mode 100644
index 0000000..e1ed46e
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/download_times_over_limit.html
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 账户下载余额超过阈值通知
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 尊敬的用户{{ username }},您好:
+
+ |
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 您当前账户下载次数仅剩 {{ user_obj.download_times }}
+ |
+
+
+
+
+ 已超过您设置的阈值 {{ user_obj.notify_available_downloads }}
+ |
+
+
+
+
+ 为了避免业务使用,望您尽快充值!
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/pay_success.html b/fir_ser/common/libs/sendmsg/templates/pay_success.html
new file mode 100644
index 0000000..64b6707
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/pay_success.html
@@ -0,0 +1,321 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 充值到账通知
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 尊敬的用户{{ username }},您好:
+
+ |
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 您已经成功充值,订单信息如下
+
+ |
+
+
+
+
+ 订单号:{{ order_obj.order_number }}
+ |
+
+
+
+
+ 支付时间:{{ order_obj.pay_time }}
+ |
+
+
+
+
+ 支付方式:{{ order_obj.get_payment_type_display }}
+ |
+
+
+
+
+ 订单信息:您充值了 {{ order_obj.actual_download_times }}
+ 下载次数,【赠送 {{ order_obj.actual_download_gift_times }}】
+
+ |
+
+
+
+
+ 备注:{{ order_obj.description }}
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/userinfo/change_userinfo.html b/fir_ser/common/libs/sendmsg/templates/userinfo/change_userinfo.html
new file mode 100644
index 0000000..08b8718
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/userinfo/change_userinfo.html
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 重要信息变更验证码
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 您的验证码:【{{ code }}】
+
+ |
+
+
+
+
+ 操作时间:{{ now_time }}
+ |
+
+
+
+
+ 备注:您正在尝试变更重要信息,请妥善保管账户信息
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/userinfo/code_notify.html b/fir_ser/common/libs/sendmsg/templates/userinfo/code_notify.html
new file mode 100644
index 0000000..31b7f0d
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/userinfo/code_notify.html
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 验证码通知
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 您的验证码:【{{ code }}】
+
+ |
+
+
+
+
+ 操作时间:{{ now_time }}
+ |
+
+
+
+
+ 备注:若非本人操作,请忽略
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/userinfo/login_code.html b/fir_ser/common/libs/sendmsg/templates/userinfo/login_code.html
new file mode 100644
index 0000000..7f55a48
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/userinfo/login_code.html
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 登录验证码
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 您的验证码:【{{ code }}】
+
+ |
+
+
+
+
+ 操作时间:{{ now_time }}
+ |
+
+
+
+
+ 备注:您正在进行登录操作,若非本人操作,请勿泄露
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/userinfo/register_code.html b/fir_ser/common/libs/sendmsg/templates/userinfo/register_code.html
new file mode 100644
index 0000000..a199ff4
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/userinfo/register_code.html
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 注册验证码
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 您的验证码:【{{ code }}】
+
+ |
+
+
+
+
+ 操作时间:{{ now_time }}
+ |
+
+
+
+
+ 备注:您正在注册成为新用户,感谢您的支持
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/userinfo/reset_password.html b/fir_ser/common/libs/sendmsg/templates/userinfo/reset_password.html
new file mode 100644
index 0000000..58859a1
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/userinfo/reset_password.html
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 重置密码通知
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 您的新密码:【{{ code }}】
+
+ |
+
+
+
+
+ 操作时间:{{ now_time }}
+ |
+
+
+
+
+ 备注:您的密码已经重置成功,请用新密码登录,并妥善保管账户信息
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/xsign/app_sign_failed.html b/fir_ser/common/libs/sendmsg/templates/xsign/app_sign_failed.html
new file mode 100644
index 0000000..496311e
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/xsign/app_sign_failed.html
@@ -0,0 +1,308 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 应用【{{ app_obj.name }}】签名失败通知
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 尊敬的用户{{ username }},您好:
+
+ |
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 应用【{{ app_obj.name }}】签名失败了
+
+ |
+
+
+
+
+ 签名时间:{{ now_time }}
+ |
+
+
+
+
+ 失败原因:开发者 {{ developer_obj.issuer_id }} 状态
+ {developer_obj.get_status_display()}
+
+ |
+
+
+
+
+ 开发者备注:{{ developer_obj.description }}。请登录后台查看具体失败信息
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/xsign/app_sign_over_limit.html b/fir_ser/common/libs/sendmsg/templates/xsign/app_sign_over_limit.html
new file mode 100644
index 0000000..95372a6
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/xsign/app_sign_over_limit.html
@@ -0,0 +1,299 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 应用限额-签名失败通知
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 尊敬的用户{{ username }},您好:
+
+ |
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 应用【{{ app_obj.name }}】签名失败了
+
+ |
+
+
+
+
+ 签名时间:{{ now_time }}
+ |
+
+
+
+
+ 失败原因:该应用已经使用设备数 {{ used_num }},已超过您设置该应用的签名限额 {{ limit_number }},当前已经无法安装新设备,为了避免业务使用,您可以修改该应用签名限额
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/xsign/apple_developer_cert_expired.html b/fir_ser/common/libs/sendmsg/templates/xsign/apple_developer_cert_expired.html
new file mode 100644
index 0000000..a8ecc5d
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/xsign/apple_developer_cert_expired.html
@@ -0,0 +1,312 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 苹果开发者证书即将到期通知
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 尊敬的用户{{ username }},您好:
+
+ |
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 你以下苹果开发者账号证书即将到期
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+ 苹果开发者ID |
+ 苹果开发者备注 |
+ 证书到期时间 |
+ {% for developer_obj in developer_obj_list %}
+
+ {{ developer_obj.issuer_id }} |
+ {{ developer_obj.description }} |
+ {{ developer_obj.cert_expire_time }} |
+ {% endfor %}
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/xsign/apple_developer_devices_over_limit.html b/fir_ser/common/libs/sendmsg/templates/xsign/apple_developer_devices_over_limit.html
new file mode 100644
index 0000000..44b83be
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/xsign/apple_developer_devices_over_limit.html
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 账户超级签名余额超过阈值通知
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 尊敬的用户{{ username }},您好:
+
+ |
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 您当前账户超级签名可用设备仅剩 {{ device_count }}
+ |
+
+
+
+
+ 已超过您设置的阈值 {{ user_obj.notify_available_signs }}
+ |
+
+
+
+
+ 为了避免业务使用,望您尽快添加苹果开发者!
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/xsign/apple_developer_unavailable.html b/fir_ser/common/libs/sendmsg/templates/xsign/apple_developer_unavailable.html
new file mode 100644
index 0000000..5d74172
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/xsign/apple_developer_unavailable.html
@@ -0,0 +1,299 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 超级签名设备余额不足-签名失败通知
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 尊敬的用户{{ username }},您好:
+
+ |
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 应用【{{ app_obj.name }}】签名失败了
+
+ |
+
+
+
+
+ 签名时间:{{ now_time }}
+ |
+
+
+
+
+ 失败原因:可用设备量已经不足或超限,请添加新的苹果开发者或修改开发者设备数量
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/libs/sendmsg/templates/xsign/timing_task_notify.html b/fir_ser/common/libs/sendmsg/templates/xsign/timing_task_notify.html
new file mode 100644
index 0000000..8c772fa
--- /dev/null
+++ b/fir_ser/common/libs/sendmsg/templates/xsign/timing_task_notify.html
@@ -0,0 +1,370 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 苹果开发者定时任务报告
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 尊敬的用户{{ username }},您好:
+
+ |
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 设备数消耗统计
+
+ |
+
+
+
+
+ 当前正常设备总量:{{ developer_used_info.used_sign_number }}
+ |
+
+
+
+
+ 已使用:【平台:{{ developer_used_info.used_number }}
+ 】【其他:{{ developer_used_info.can_other_used }}】
+
+ |
+
+
+
+
+ 还剩:{{ developer_used_info.can_sign_number }} 可用
+
+ |
+
+
+
+
+ 昨天消耗设备数:{{ yesterday_used_number }}
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ 定时任务检测已经执行完成
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+ 苹果开发者ID |
+ 苹果开发者备注 |
+ 苹果开发者状态 |
+ {% for developer_obj in developer_obj_list %}
+
+ {{ developer_obj.issuer_id }} |
+ {{ developer_obj.description }} |
+ {{ developer_obj.get_status_display }} |
+ {% endfor %}
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fir_ser/common/notify/notify.py b/fir_ser/common/notify/notify.py
index 9588424..e4692b0 100644
--- a/fir_ser/common/notify/notify.py
+++ b/fir_ser/common/notify/notify.py
@@ -8,8 +8,10 @@ import logging
from api.utils.modelutils import get_notify_wx_queryset, get_wx_nickname
from common.base.baseutils import get_format_time
-from common.core.sysconfig import Config
from common.libs.mp.wechat import WxTemplateMsg
+from common.libs.sendmsg.template_content import get_pay_success_html_content, get_sign_failed_html_content, \
+ get_sign_unavailable_developer_html_content, get_sign_app_over_limit_html_content, \
+ get_check_developer_report_html_content
from common.notify.utils import notify_by_email
logger = logging.getLogger(__name__)
@@ -27,7 +29,6 @@ def pay_success_notify(user_obj, order_obj):
title = f'{order_obj.actual_download_times} 下载次数'
if order_obj.actual_download_gift_times > 0:
title = f'{title} 【赠送 {order_obj.actual_download_gift_times}】'
- msg = f"用户 {user_obj.first_name} 您好,{order_obj.description}。您购买了 {title}。感谢有你!"
for wx_user_obj in get_notify_wx_queryset(user_obj, message_type):
res = WxTemplateMsg(wx_user_obj.openid, get_wx_nickname(wx_user_obj.openid)).pay_success_msg(
title,
@@ -37,7 +38,7 @@ def pay_success_notify(user_obj, order_obj):
order_obj.order_number, order_obj.description)
logger.info(f'user_obj {user_obj} weixin notify pay success result: {res}')
- notify_by_email(user_obj, message_type, msg)
+ notify_by_email(user_obj, message_type, get_pay_success_html_content(user_obj, order_obj))
def sign_failed_notify(user_obj, developer_obj, app_obj):
@@ -48,9 +49,6 @@ def sign_failed_notify(user_obj, developer_obj, app_obj):
message_type = 3
now_time = get_format_time().replace('_', ' ')
- msg = Config.MSG_ERROR_DEVELOPER % (
- user_obj.first_name, app_obj.name, now_time, developer_obj.issuer_id, developer_obj.description)
-
for wx_user_obj in get_notify_wx_queryset(user_obj, message_type):
res = WxTemplateMsg(wx_user_obj.openid, get_wx_nickname(wx_user_obj.openid)).operate_failed_msg(
user_obj.first_name, f'应用 {app_obj.name} 签名失败了',
@@ -58,7 +56,7 @@ def sign_failed_notify(user_obj, developer_obj, app_obj):
f'开发者备注:{developer_obj.description},请登录后台查看具体信息')
logger.info(f'user_obj {user_obj} sign_failed_notify result: {res}')
- notify_by_email(user_obj, message_type, msg)
+ notify_by_email(user_obj, message_type, get_sign_failed_html_content(user_obj, app_obj, developer_obj, now_time))
def sign_unavailable_developer_notify(user_obj, app_obj):
@@ -68,14 +66,13 @@ def sign_unavailable_developer_notify(user_obj, app_obj):
"""
message_type = 3
now_time = get_format_time().replace('_', ' ')
- msg = Config.MSG_NOT_EXIST_DEVELOPER % (user_obj.first_name, app_obj.name, now_time)
for wx_user_obj in get_notify_wx_queryset(user_obj, message_type):
res = WxTemplateMsg(wx_user_obj.openid, get_wx_nickname(wx_user_obj.openid)).operate_failed_msg(
user_obj.first_name, f'应用 {app_obj.name} 签名失败了',
f'苹果开发者总设备量已经超限', now_time, '添加新的苹果开发者或者修改开发者设备数量')
logger.info(f'user_obj {user_obj} sign_unavailable_developer result: {res}')
- notify_by_email(user_obj, message_type, msg)
+ notify_by_email(user_obj, message_type, get_sign_unavailable_developer_html_content(user_obj, app_obj, now_time))
def sign_app_over_limit_notify(user_obj, app_obj, used_num, limit_number):
@@ -89,16 +86,16 @@ def sign_app_over_limit_notify(user_obj, app_obj, used_num, limit_number):
"""
message_type = 2
now_time = get_format_time().replace('_', ' ')
- msg = Config.MSG_SING_APP_OVER_LIMIT % (user_obj.first_name, app_obj.name, now_time, used_num, limit_number)
for wx_user_obj in get_notify_wx_queryset(user_obj, message_type):
res = WxTemplateMsg(wx_user_obj.openid, get_wx_nickname(wx_user_obj.openid)).operate_failed_msg(
user_obj.first_name, f'应用 {app_obj.name} 签名失败了', f'超过该应用的签名限额 {limit_number}', now_time,
f'该应用已经使用设备数 {used_num},已超过您设置该应用的签名限额 {limit_number},当前已经无法安装新设备,为了避免业务使用,您可以修改该应用签名限额')
logger.info(f'user_obj {user_obj} sign devices not enough result: {res}')
- notify_by_email(user_obj, message_type, msg)
+ notify_by_email(user_obj, message_type,
+ get_sign_app_over_limit_html_content(user_obj, app_obj, now_time, used_num, limit_number))
-def check_developer_status_notify(user_obj, developer_obj_list, content):
+def check_developer_status_notify(user_obj, developer_obj_list, developer_used_info, yesterday_used_number):
"""
7, '系统提醒'
@@ -121,4 +118,6 @@ def check_developer_status_notify(user_obj, developer_obj_list, content):
res = WxTemplateMsg(wx_user_obj.openid, nick_name).task_finished_msg(title, '苹果开发者状态检测', '完成',
now_time, description)
logger.info(f'user_obj {user_obj} sign devices not enough result: {res}')
- notify_by_email(user_obj, message_type, html=content)
+ notify_by_email(user_obj, message_type,
+ get_check_developer_report_html_content(user_obj, developer_obj_list, developer_used_info,
+ yesterday_used_number))
diff --git a/fir_ser/common/notify/ntasks.py b/fir_ser/common/notify/ntasks.py
index de3c1f9..b25ddd2 100644
--- a/fir_ser/common/notify/ntasks.py
+++ b/fir_ser/common/notify/ntasks.py
@@ -12,6 +12,9 @@ from common.base.magic import magic_wrapper, magic_notify
from common.cache.storage import NotifyLoopCache
from common.core.sysconfig import Config
from common.libs.mp.wechat import WxTemplateMsg
+from common.libs.sendmsg.template_content import get_user_download_times_not_enough_html_content, \
+ get_developer_devices_not_enough_html_content, get_developer_cert_expired_html_content, \
+ get_user_download_times_over_limit_html_content
from common.notify.utils import notify_by_email
from xsign.models import AppIOSDeveloperInfo
from xsign.utils.modelutils import get_developer_devices
@@ -31,7 +34,7 @@ def download_times_not_enough(user_obj, msg):
res = WxTemplateMsg(wx_user_obj.openid, get_wx_nickname(wx_user_obj.openid)).download_times_not_enough_msg(
user_obj.first_name, user_obj.download_times, msg)
logger.info(f'user_obj {user_obj} download times not enough result: {res}')
- notify_by_email(user_obj, message_type, msg)
+ notify_by_email(user_obj, message_type, get_user_download_times_not_enough_html_content(user_obj))
def apple_developer_devices_not_enough(user_obj, device_count):
@@ -47,7 +50,7 @@ def apple_developer_devices_not_enough(user_obj, device_count):
get_wx_nickname(wx_user_obj.openid)).apple_developer_devices_not_enough_msg(
user_obj.first_name, device_count, msg)
logger.info(f'user_obj {user_obj} sign devices not enough result: {res}')
- notify_by_email(user_obj, message_type, msg)
+ notify_by_email(user_obj, message_type, get_developer_devices_not_enough_html_content(user_obj, device_count))
def apple_developer_cert_expired(user_obj, developer_queryset):
@@ -64,11 +67,9 @@ def apple_developer_cert_expired(user_obj, developer_queryset):
if developer_count == 1:
issuer_id = developer_obj.issuer_id
cert_id = developer_obj.certid
- msg = f"用户 {user_obj.first_name} 您好,您苹果开发者 {issuer_id} ,证书 {cert_id} 即将到期,到期时间 {expired_time},为了保证您开发者可用,请您尽快更新开发者证书,感谢您的关注"
else:
issuer_id = f'{developer_obj.issuer_id} 等 {developer_count} 个开发者ID'
cert_id = f'{developer_obj.certid} 等 {developer_count} 个证书ID'
- msg = f"用户 {user_obj.first_name} 您好,您苹果开发者 {issuer_id} ,证书 {cert_id} 即将到期,到期时间 {expired_time},为了保证您开发者可用,请您尽快更新开发者证书,感谢您的关注 "
for wx_user_obj in get_notify_wx_queryset(user_obj, message_type):
res = WxTemplateMsg(wx_user_obj.openid, get_wx_nickname(wx_user_obj.openid)).cert_expired_msg(issuer_id,
@@ -76,7 +77,7 @@ def apple_developer_cert_expired(user_obj, developer_queryset):
expired_time)
logger.info(f'user_obj {user_obj} apple developer cert expired result: {res}')
- notify_by_email(user_obj, message_type, msg)
+ notify_by_email(user_obj, message_type, get_developer_cert_expired_html_content(user_obj, developer_queryset))
def check_user_download_times(user_obj, days=None):
@@ -84,7 +85,7 @@ def check_user_download_times(user_obj, days=None):
days = [0, 3, 7]
if user_obj.notify_available_downloads == 0 or user_obj.notify_available_downloads < user_obj.download_times:
return
- msg = f"您当前账户下载次数仅剩 {user_obj.download_times},已超过您设置的阈值 {user_obj.notify_available_downloads},为了避免业务使用,望您尽快充值!"
+ msg = get_user_download_times_over_limit_html_content(user_obj)
notify_rules = [
{
'func': magic_wrapper(lambda obj: obj.download_times < obj.notify_available_downloads, user_obj),
diff --git a/fir_ser/common/notify/utils.py b/fir_ser/common/notify/utils.py
index 48bd4a9..0372dee 100644
--- a/fir_ser/common/notify/utils.py
+++ b/fir_ser/common/notify/utils.py
@@ -12,8 +12,8 @@ from common.utils.sendmsg import get_sender_email_token
logger = logging.getLogger(__name__)
-def notify_by_email(user_obj, message_type, msg='', html=None):
+def notify_by_email(user_obj, message_type, html):
for notify_email in get_notify_email_queryset(user_obj, message_type):
email = notify_email.get('email')
if email:
- get_sender_email_token('email', email, 'msg', html if html else f'您好,{user_obj.first_name},{msg}')
+ get_sender_email_token('email', email, 'msg', html)
diff --git a/fir_ser/config.py b/fir_ser/config.py
index 54e88a7..28f1e6f 100644
--- a/fir_ser/config.py
+++ b/fir_ser/config.py
@@ -205,11 +205,11 @@ class SENDERCONF(object):
'form': 'fly分发平台 ',
'subject': '%(code)s验证',
'template_code': {
- 'login': '欢迎使用FLY 应用分发平台。 您的验证码%(code)s ,您正在登录,若非本人操作,请勿泄露。',
- 'change': '欢迎使用FLY 应用分发平台。 您的验证码%(code)s ,您正在尝试变更重要信息,请妥善保管账户信息。',
- 'register': '欢迎使用FLY 应用分发平台。 您的验证码%(code)s ,您正在注册成为新用户,感谢您的支持!',
- 'password': '欢迎使用FLY 应用分发平台。 您的新密码为%(code)s , 请用新密码登录之后,及时修改密码,并妥善保管账户信息。',
- 'common': '欢迎使用FLY 应用分发平台。 您的验证码%(code)s , 若非本人操作,请忽略。'
+ 'login': 'common.libs.sendmsg.template_content.get_userinfo_login_code_html_content',
+ 'change': 'common.libs.sendmsg.template_content.get_userinfo_change_code_html_content',
+ 'register': 'common.libs.sendmsg.template_content.get_userinfo_register_code_html_content',
+ 'password': 'common.libs.sendmsg.template_content.get_userinfo_reset_pwd_html_content',
+ 'common': 'common.libs.sendmsg.template_content.get_code_notify_html_content'
}
},
'active': True
@@ -379,13 +379,6 @@ bIX1aWjPxirQX9mzaL3oEQI=
]
-class MSGCONF(object):
- MSG_NOT_EXIST_DEVELOPER = '用户 %s 你好,应用 %s 在 %s 签名失败了,苹果开发者总设备量已经超限,请添加新的苹果开发者或者修改开发者设备数量。感谢有你!'
- MSG_ERROR_DEVELOPER = '用户 %s 你好,应用 %s 在 %s 签名失败了,苹果开发者【%s】开发者备注【%s】 信息异常,请重新检查苹果开发者状态是否正常。感谢有你!'
- MSG_AUTO_CHECK_DEVELOPER = '用户 %s 你好,苹果开发者 %s 信息异常,请重新检查苹果开发者状态是否正常。感谢有你!'
- MSG_SING_APP_OVER_LIMIT = '用户 %s 你好,应用 %s 在 %s 签名失败了,该应用已经使用设备数 %s,超过您设置的签名限额 %s,当前已经无法安装新设备,为了避免业务使用,您可以修改该应用签名限额 '
-
-
class DOWNLOADTIMESCONF(object):
USER_FREE_DOWNLOAD_TIMES = 5
AUTH_USER_FREE_DOWNLOAD_TIMES = 10
diff --git a/fir_ser/xsign/utils/ctasks.py b/fir_ser/xsign/utils/ctasks.py
index 17dd07d..f3eb6ee 100644
--- a/fir_ser/xsign/utils/ctasks.py
+++ b/fir_ser/xsign/utils/ctasks.py
@@ -10,8 +10,6 @@ import random
import time
from concurrent.futures import ThreadPoolExecutor
-from django.template import loader
-
from common.core.sysconfig import Config
from common.notify.notify import check_developer_status_notify
from fir_ser.settings import SUPER_SIGN_ROOT, SYNC_CACHE_TO_DATABASE
@@ -76,15 +74,7 @@ def auto_check_ios_developer_active():
yesterday_used_number = APPSuperSignUsedInfo.objects.filter(developerid__user_id=userinfo,
created_time__range=[start_time, end_time]).count()
developer_obj_list = sorted(developer_obj_list, key=lambda obj: obj.status)
- content = loader.render_to_string('check_developer.html',
- {
- 'username': userinfo.first_name,
- 'developer_obj_list': developer_obj_list,
- 'developer_used_info': developer_used_info,
- 'yesterday_used_number': yesterday_used_number,
- })
- # send_ios_developer_active_status(userinfo, content)
- check_developer_status_notify(userinfo, developer_obj_list, content)
+ check_developer_status_notify(userinfo, developer_obj_list, developer_used_info, yesterday_used_number)
def auto_clean_sign_log(clean_day=30 * 6):
diff --git a/fir_ser/xsign/utils/iproxy.py b/fir_ser/xsign/utils/iproxy.py
index 09dbddb..d6eae95 100644
--- a/fir_ser/xsign/utils/iproxy.py
+++ b/fir_ser/xsign/utils/iproxy.py
@@ -9,6 +9,7 @@ import time
from concurrent.futures import ThreadPoolExecutor
import requests
+from django.core.cache import cache
from common.cache.storage import IpProxyListCache, IpProxyActiveCache
from common.core.sysconfig import Config
@@ -61,7 +62,10 @@ def get_proxy_ip_from_cache(issuer_id, change_ip=False):
list_proxy_cache = IpProxyListCache()
ip_proxy_result = list_proxy_cache.get_storage_cache()
if not ip_proxy_result:
- ip_proxy_result = get_best_proxy_ips()
+ with cache.lock('get_best_proxy_ips_locker', timeout=360):
+ ip_proxy_result = list_proxy_cache.get_storage_cache()
+ if not ip_proxy_result:
+ ip_proxy_result = get_best_proxy_ips()
if change_ip and ip_proxy_result:
try:
diff --git a/fir_ser/xsign/utils/utils.py b/fir_ser/xsign/utils/utils.py
index 1013dc9..cf57006 100644
--- a/fir_ser/xsign/utils/utils.py
+++ b/fir_ser/xsign/utils/utils.py
@@ -6,7 +6,6 @@
import logging
from api.utils.utils import delete_local_files
-from common.utils.sendmsg import get_sender_email_token
from common.utils.storage import Storage
from xsign.models import APPToDeveloper
@@ -21,12 +20,3 @@ def delete_app_to_dev_and_file(developer_obj, app_id):
storage = Storage(developer_obj.user_id)
storage.delete_file(binary_file)
app_to_developer_obj.delete()
-
-
-def send_ios_developer_active_status(user_info, msg):
- act = 'email'
- email = user_info.email
- if email:
- get_sender_email_token(act, email, 'msg', msg)
- else:
- logger.warning(f"user {user_info} has no email. so {msg} can't send!")
diff --git a/mailhtml/build.js b/mailhtml/build.js
new file mode 100644
index 0000000..46b2598
--- /dev/null
+++ b/mailhtml/build.js
@@ -0,0 +1,61 @@
+const spawn = require("child_process").spawn;
+const path = require('path')
+const fs = require('fs')
+const cmdPath = path.resolve('./node_modules/.bin/mjml.cmd')
+
+
+function build_html(file_full_path, buildPath) {
+ let output_path = file_full_path.replace('mjml.html','html')
+ output_path = path.join(buildPath,path.basename(output_path))
+ return new Promise(function(resolve, reject) {
+ let result = spawn(cmdPath, [file_full_path,'-o',output_path]);
+ result.on('close', function(code) {
+ if(code === 0){
+ console.log('build success. output:'+output_path)
+ }else {
+ console.log('child process exited with code :' + code);
+ }
+ });
+ result.stdout.on('data', function(data) {
+ console.log('stdout: ' + data);
+ });
+ result.stderr.on('data', function(data) {
+ console.log('stderr: ' + data);
+ reject(new Error(data.toString()));
+ });
+ resolve();
+ });
+}
+let filePath = path.resolve('./src')
+let buildPath = path.resolve('./build')
+fs.exists(buildPath,function (exists) {
+ if(!exists){
+ fs.mkdir(buildPath,res=>{
+ })
+ }else {
+ fs.rmdir(buildPath,{recursive:true},res=>{
+ fs.mkdir(buildPath,res=>{
+ })
+ })
+ }
+})
+fs.readdir(filePath,function (err,files) {
+ if (err) {
+ console.warn(err, "读取文件夹错误!")
+ } else {
+ files.forEach(function(filename) {
+ //获取当前文件的绝对路径
+ let file_full_path = path.join(filePath, filename);
+ fs.stat(file_full_path, function(error, stats) {
+ if (error) {
+ console.warn('获取文件stats失败');
+ } else {
+ if(stats.isFile() && file_full_path.endsWith('.mjml.html')){
+ build_html(file_full_path, buildPath).then()
+ }
+ }
+ })
+
+ })
+ }
+})
diff --git a/mailhtml/package-lock.json b/mailhtml/package-lock.json
new file mode 100644
index 0000000..b1d78c1
--- /dev/null
+++ b/mailhtml/package-lock.json
@@ -0,0 +1,1115 @@
+{
+ "name": "mailhtml",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.17.9",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz",
+ "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==",
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA=="
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+ "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+ },
+ "binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
+ },
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "camel-case": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
+ "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
+ "requires": {
+ "no-case": "^2.2.0",
+ "upper-case": "^1.1.1"
+ }
+ },
+ "cheerio": {
+ "version": "1.0.0-rc.10",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz",
+ "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==",
+ "requires": {
+ "cheerio-select": "^1.5.0",
+ "dom-serializer": "^1.3.2",
+ "domhandler": "^4.2.0",
+ "htmlparser2": "^6.1.0",
+ "parse5": "^6.0.1",
+ "parse5-htmlparser2-tree-adapter": "^6.0.1",
+ "tslib": "^2.2.0"
+ }
+ },
+ "cheerio-select": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.6.0.tgz",
+ "integrity": "sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g==",
+ "requires": {
+ "css-select": "^4.3.0",
+ "css-what": "^6.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.3.1",
+ "domutils": "^2.8.0"
+ }
+ },
+ "chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "requires": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ }
+ },
+ "clean-css": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
+ "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==",
+ "requires": {
+ "source-map": "~0.6.0"
+ }
+ },
+ "cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "config-chain": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "requires": {
+ "ini": "^1.3.4",
+ "proto-list": "~1.2.1"
+ }
+ },
+ "css-select": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+ "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+ "requires": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.0.1",
+ "domhandler": "^4.3.1",
+ "domutils": "^2.8.0",
+ "nth-check": "^2.0.1"
+ }
+ },
+ "css-what": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="
+ },
+ "detect-node": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
+ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw=="
+ },
+ "dom-serializer": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+ "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ }
+ },
+ "domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
+ },
+ "domhandler": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+ "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "requires": {
+ "domelementtype": "^2.2.0"
+ }
+ },
+ "domutils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+ "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "requires": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
+ }
+ },
+ "editorconfig": {
+ "version": "0.15.3",
+ "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz",
+ "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==",
+ "requires": {
+ "commander": "^2.19.0",
+ "lru-cache": "^4.1.5",
+ "semver": "^5.6.0",
+ "sigmund": "^1.0.1"
+ }
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ },
+ "entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
+ },
+ "escape-goat": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz",
+ "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw=="
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "optional": true
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+ },
+ "glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
+ },
+ "html-minifier": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz",
+ "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==",
+ "requires": {
+ "camel-case": "^3.0.0",
+ "clean-css": "^4.2.1",
+ "commander": "^2.19.0",
+ "he": "^1.2.0",
+ "param-case": "^2.1.1",
+ "relateurl": "^0.2.7",
+ "uglify-js": "^3.5.1"
+ }
+ },
+ "htmlparser2": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+ "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.0.0",
+ "domutils": "^2.5.2",
+ "entities": "^2.0.0"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
+ },
+ "js-beautify": {
+ "version": "1.14.2",
+ "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.2.tgz",
+ "integrity": "sha512-H85kX95a53os+q1OCqtYe8AXAmgy3BvtysA/V83S3fdhznm6WlUpGi14DqSPbKFsL3dXZFXYl7YQwW9U1+76ng==",
+ "requires": {
+ "config-chain": "^1.1.12",
+ "editorconfig": "^0.15.3",
+ "glob": "^7.1.3",
+ "nopt": "^5.0.0"
+ }
+ },
+ "juice": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/juice/-/juice-7.0.0.tgz",
+ "integrity": "sha512-AjKQX31KKN+uJs+zaf+GW8mBO/f/0NqSh2moTMyvwBY+4/lXIYTU8D8I2h6BAV3Xnz6GGsbalUyFqbYMe+Vh+Q==",
+ "requires": {
+ "cheerio": "^1.0.0-rc.3",
+ "commander": "^5.1.0",
+ "mensch": "^0.3.4",
+ "slick": "^1.12.2",
+ "web-resource-inliner": "^5.0.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
+ "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="
+ }
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw="
+ },
+ "lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "mensch": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz",
+ "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g=="
+ },
+ "mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mjml": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml/-/mjml-4.12.0.tgz",
+ "integrity": "sha512-uWDu1pPQVyoX4iKIrM02J6qOBN6PC1rSMP64DKi2qGU4dpOztVgvTBh6JttIbINV4ZiALtpeGu+jeEUqp2ROXA==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "mjml-cli": "4.12.0",
+ "mjml-core": "4.12.0",
+ "mjml-migrate": "4.12.0",
+ "mjml-preset-core": "4.12.0",
+ "mjml-validator": "4.12.0"
+ }
+ },
+ "mjml-accordion": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-4.12.0.tgz",
+ "integrity": "sha512-vqBk4NhXN+w6F3c5vnLxkvgneREpkwTzZpbxtMzpNqkUW2yei0oSQ26j/wLgXYTaX+4Czp+oVr0cnNxjyCZHjA==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-body": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-4.12.0.tgz",
+ "integrity": "sha512-IQBAHhdRKsNUXat+oxvRTjVJ1qzTRkNjFe/mtD/Pbn9olUnQmV+RKxnkqRZf7QtiTxVIOGC4kU9VLPjNymsFXQ==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-button": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-4.12.0.tgz",
+ "integrity": "sha512-XJfLP+mHvCr6Ky16ooYz5+8ODkf10+ATyvENCKyrof+rietr5WxN2FxWCZA9Orq20OE74/hvaOeZZdkxwtsXig==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-carousel": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-4.12.0.tgz",
+ "integrity": "sha512-vQ5Aqvix9mbAE0GspxIDpKK4dVMRuKFO3qV6N/CkrIAOe4+2CKV4AMn2fWUvQEx6hA6CGxayeLkI7E0hNOWcZA==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-cli": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-4.12.0.tgz",
+ "integrity": "sha512-//Y4XsN6aFgpZtDbQZRu4qe+CQzGWV3i5K3rC1dwPcdtpDMsXBPKiwIZFrQxpRVBwxs0hU4ZBQOMtvYZkoicdQ==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "chokidar": "^3.0.0",
+ "glob": "^7.1.1",
+ "html-minifier": "^4.0.0",
+ "js-beautify": "^1.6.14",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0",
+ "mjml-migrate": "4.12.0",
+ "mjml-parser-xml": "4.12.0",
+ "mjml-validator": "4.12.0",
+ "yargs": "^16.1.0"
+ }
+ },
+ "mjml-column": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-4.12.0.tgz",
+ "integrity": "sha512-Ub/7ov2B1T2jfSpxvF61o3UCU4gGDFUqIelr7ghuazLc2KvTwdHYeR8mWt8l8RBM6zZiWjkYEFMP22ty7WXztg==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-core": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-4.12.0.tgz",
+ "integrity": "sha512-B3gUkV3kFN1IlzIV3GnpWBmE21XHH5ARyydMxacR75iC53PvJ9c50hr6DWLGdrrDCC6Fdud8jTmgD9dnWPmJhQ==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "cheerio": "1.0.0-rc.10",
+ "detect-node": "2.0.4",
+ "html-minifier": "^4.0.0",
+ "js-beautify": "^1.6.14",
+ "juice": "^7.0.0",
+ "lodash": "^4.17.21",
+ "mjml-migrate": "4.12.0",
+ "mjml-parser-xml": "4.12.0",
+ "mjml-validator": "4.12.0"
+ }
+ },
+ "mjml-divider": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-4.12.0.tgz",
+ "integrity": "sha512-L87iqrhVS+PnUInYbXK4lcTQcHfWMTL7ZqDL9XEMBywzX8cCfviLNMbqmLCO2HD8nMPVMRbcE32H04T6LyZ2qw==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-group": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-4.12.0.tgz",
+ "integrity": "sha512-Rl7Iydd7M2SnbH1ItIi07hYY+FrEai5c6kYMKbcFWAuNupCuvUThuhx1AphMPCZFMLbbPSKNWMarBkWhepS7cw==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-head": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-4.12.0.tgz",
+ "integrity": "sha512-LcI4ykOB6nMV5W//tF9S1unlXxexfNZUnnyZ2OOzP1V7J5poLXdKXqB8XATN2YGGTsDZ5Q/5V1KO+NnjpW7zSw==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-head-attributes": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-4.12.0.tgz",
+ "integrity": "sha512-tRwKUzIrtcw1FGy8Xpy4vrFo0u2daZgqx3X0cM5WWrGFcKe7ZdjNEAkU/3w+WsFjeMcb0fHdKvd+sxBjPJ6fpA==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-head-breakpoint": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-4.12.0.tgz",
+ "integrity": "sha512-BVVbvAIcIu49P1EJkEPPIY8Gu4GleyzpkdddqD3ihAPn3Pz07SEsFlHvI35eCszuaJeeMbSSxLrsF4m+aQQlvw==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-head-font": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-4.12.0.tgz",
+ "integrity": "sha512-ja5sWbGOIr1gF/7IIPzrgOlWYiKk57BC8JWYRANV7CxNKa635sd6aBJHbzXv1A6Ph+zH5KtE0MSQCK8n49BIsw==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-head-html-attributes": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-head-html-attributes/-/mjml-head-html-attributes-4.12.0.tgz",
+ "integrity": "sha512-XJesJuW9uzlNN5w/S7t5ZquSVDay7BehOKmIZKMwKn1y0SJBXiakcwt9M9hhF0HB189Bew0gpGt3m7QYvTez8g==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-head-preview": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-4.12.0.tgz",
+ "integrity": "sha512-pr02ZkxwU6/LWhrL3xP/hLrUXx27I1FnfgaYjgvMjh6pMURuy7W+W8BrNJKeyXZo685b2A5lNFDJV7rCJ6HrEQ==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-head-style": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-head-style/-/mjml-head-style-4.12.0.tgz",
+ "integrity": "sha512-64IVdJ2Xl000SrwLt4cebl+MiZcino/ywMkuLQ/c48XeR6pkvbjXYAInWsdlMG1y041n1bOZICNnQQc4xhNJrw==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-head-title": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-head-title/-/mjml-head-title-4.12.0.tgz",
+ "integrity": "sha512-c7thJUmNLIdVy1ftLbYUjchHwrIfAb9SHdbuVQHdtQz45a3Ni2nie4AWxF/srn90k8q/uEKtQq1taOa4f71Zug==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-hero": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-hero/-/mjml-hero-4.12.0.tgz",
+ "integrity": "sha512-j87DgSAyLzMMuNtVqR1okkI/orKnvZoR7i+RsA1yueNql9dZtnw3Ezy8cas8MJaAoGOmqIy9AqGRJIr82w4mxQ==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-image": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-4.12.0.tgz",
+ "integrity": "sha512-P77M+PLLNn7QvGhL8sx+6yzkQbEMxIQO3yxqUC+x8Ie8kXS8phSNGcqx8qfhdN7p7sQ3CZdOIZSXkG7RRAF94w==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-migrate": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-migrate/-/mjml-migrate-4.12.0.tgz",
+ "integrity": "sha512-KDdPkuOzL9CAekY0CslM0Yqiomk4TubNMszw6UFfylp5xRA3CfBo0HdGcnewHBkZ8+isjPlzDWf3n+NkU11OiA==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "js-beautify": "^1.6.14",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0",
+ "mjml-parser-xml": "4.12.0",
+ "yargs": "^16.1.0"
+ }
+ },
+ "mjml-navbar": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-4.12.0.tgz",
+ "integrity": "sha512-TWKV5lFgwUvRbG+FNz6Uo7mGPJRU/BK1v0BeQr1e5Ykft4052iYIuv2XNwRkeoORmLT+7AN8FbkP+TVBpflbWw==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-parser-xml": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-parser-xml/-/mjml-parser-xml-4.12.0.tgz",
+ "integrity": "sha512-cmCcvoiirH0kuCglGAjwBVfDrlnqS3e83uBwPN6wDN6IfxSgsPT6IV0vRfcJERsr2ThpFjvoSq4GmYi9oCUSMw==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "detect-node": "2.0.4",
+ "htmlparser2": "^4.1.0",
+ "lodash": "^4.17.15"
+ },
+ "dependencies": {
+ "domhandler": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz",
+ "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==",
+ "requires": {
+ "domelementtype": "^2.0.1"
+ }
+ },
+ "htmlparser2": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz",
+ "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==",
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^3.0.0",
+ "domutils": "^2.0.0",
+ "entities": "^2.0.0"
+ }
+ }
+ }
+ },
+ "mjml-preset-core": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-4.12.0.tgz",
+ "integrity": "sha512-zoiCKcl/bK43ltr2J8dY9Qg5fcB3TbhaWcTG84oGYWdii5WEkKTXj5hpP1ss1XqdOGMNLij/HVwmli+xQCo6FQ==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "mjml-accordion": "4.12.0",
+ "mjml-body": "4.12.0",
+ "mjml-button": "4.12.0",
+ "mjml-carousel": "4.12.0",
+ "mjml-column": "4.12.0",
+ "mjml-divider": "4.12.0",
+ "mjml-group": "4.12.0",
+ "mjml-head": "4.12.0",
+ "mjml-head-attributes": "4.12.0",
+ "mjml-head-breakpoint": "4.12.0",
+ "mjml-head-font": "4.12.0",
+ "mjml-head-html-attributes": "4.12.0",
+ "mjml-head-preview": "4.12.0",
+ "mjml-head-style": "4.12.0",
+ "mjml-head-title": "4.12.0",
+ "mjml-hero": "4.12.0",
+ "mjml-image": "4.12.0",
+ "mjml-navbar": "4.12.0",
+ "mjml-raw": "4.12.0",
+ "mjml-section": "4.12.0",
+ "mjml-social": "4.12.0",
+ "mjml-spacer": "4.12.0",
+ "mjml-table": "4.12.0",
+ "mjml-text": "4.12.0",
+ "mjml-wrapper": "4.12.0"
+ }
+ },
+ "mjml-raw": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-4.12.0.tgz",
+ "integrity": "sha512-vQUmrEZEgu0DCca7tiPdQ/vf8GM5QyeaabbLd1rX3XCt5Mid47LCdszmVcrk1WxqNuExIw1fNyEGCCDeP2qCJg==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-section": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-4.12.0.tgz",
+ "integrity": "sha512-5BdHrAghS/XJ40t3qtLHpY3rIVuBnJXv8dGm8U+oMVAzw3L4ySk5WI+FulRkchdPFCKpeXQZjXZaX0C7pmNaIw==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-social": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-4.12.0.tgz",
+ "integrity": "sha512-eTsqJoKP65Imawh+WEX2dv4N34ItUmvIbsCeSQPhC/NG6klxDjzg5oDA1F2tZk+CPIuXVmJiauQ5/vPHLzUiVw==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-spacer": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-4.12.0.tgz",
+ "integrity": "sha512-YB+VCixcuWXDzICrGLFw7PJDkL166e4OG8IUUB2yhvd5VHtFFBc0iRksaEAumOL1r6MnXVCRq4Wcmxlzj7zOfQ==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-table": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-4.12.0.tgz",
+ "integrity": "sha512-IuLvyiJOsM6RgobuIfZuM36fJcoH8pK/A4awCLTEme0HCxEkkjzDkl4RBMK/KX53Cpor0U6oR6RlQfZcducpLg==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-text": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-4.12.0.tgz",
+ "integrity": "sha512-AFcXiQBC48ZfKKgAdU0NRS2nqftc8zLGxBtPwHNgFkuh5Lf2rWgPK6JRubNi7qhb8Sd7M8stU+LIRA5sxM1nRQ==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0"
+ }
+ },
+ "mjml-validator": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-4.12.0.tgz",
+ "integrity": "sha512-EmOScfcJJ4LdIyHnE+K4FdkryQ+c6QRV7qp+zlunAHE5AUPaBS0OrHPHuNo1sOu7g1tc+bVl7eHR4FIb0Wkzwg==",
+ "requires": {
+ "@babel/runtime": "^7.14.6"
+ }
+ },
+ "mjml-wrapper": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-4.12.0.tgz",
+ "integrity": "sha512-u0pq+A9QBLwpeF/hdv2uWZIv3Qp4wwf+CMaHZsUpb3YfOJD/6YKwLvkeA7ngE+YxwwzgtgjmIEs4eDae1evlgQ==",
+ "requires": {
+ "@babel/runtime": "^7.14.6",
+ "lodash": "^4.17.21",
+ "mjml-core": "4.12.0",
+ "mjml-section": "4.12.0"
+ }
+ },
+ "no-case": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
+ "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
+ "requires": {
+ "lower-case": "^1.1.1"
+ }
+ },
+ "node-fetch": {
+ "version": "2.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+ "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+ "requires": {
+ "whatwg-url": "^5.0.0"
+ }
+ },
+ "nopt": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+ "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
+ },
+ "nth-check": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+ "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+ "requires": {
+ "boolbase": "^1.0.0"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "param-case": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
+ "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
+ "requires": {
+ "no-case": "^2.2.0"
+ }
+ },
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
+ },
+ "parse5-htmlparser2-tree-adapter": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+ "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+ "requires": {
+ "parse5": "^6.0.1"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
+ },
+ "proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk="
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.9",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+ "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
+ },
+ "relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
+ "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
+ },
+ "slick": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz",
+ "integrity": "sha1-vQSN23TefRymkV+qSldXCzVQwtc="
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ },
+ "string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+ },
+ "tslib": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+ "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+ },
+ "uglify-js": {
+ "version": "3.15.3",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.3.tgz",
+ "integrity": "sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg=="
+ },
+ "upper-case": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
+ "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg="
+ },
+ "valid-data-url": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz",
+ "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA=="
+ },
+ "web-resource-inliner": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-5.0.0.tgz",
+ "integrity": "sha512-AIihwH+ZmdHfkJm7BjSXiEClVt4zUFqX4YlFAzjL13wLtDuUneSaFvDBTbdYRecs35SiU7iNKbMnN+++wVfb6A==",
+ "requires": {
+ "ansi-colors": "^4.1.1",
+ "escape-goat": "^3.0.0",
+ "htmlparser2": "^4.0.0",
+ "mime": "^2.4.6",
+ "node-fetch": "^2.6.0",
+ "valid-data-url": "^3.0.0"
+ },
+ "dependencies": {
+ "domhandler": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz",
+ "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==",
+ "requires": {
+ "domelementtype": "^2.0.1"
+ }
+ },
+ "htmlparser2": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz",
+ "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==",
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^3.0.0",
+ "domutils": "^2.0.0",
+ "entities": "^2.0.0"
+ }
+ }
+ }
+ },
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+ },
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+ },
+ "yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "requires": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
+ }
+ }
+}
diff --git a/mailhtml/package.json b/mailhtml/package.json
new file mode 100644
index 0000000..504173e
--- /dev/null
+++ b/mailhtml/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "mailhtml",
+ "version": "1.0.0",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "build": "build.js"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "description": "",
+ "dependencies": {
+ "mjml": "^4.12.0"
+ }
+}
diff --git a/mailhtml/src/app_sign_failed.mjml.html b/mailhtml/src/app_sign_failed.mjml.html
new file mode 100644
index 0000000..250d947
--- /dev/null
+++ b/mailhtml/src/app_sign_failed.mjml.html
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+ 应用【{{app_obj.name}}】签名失败通知
+
+
+
+
+
+
+
+
+
+ 应用【{{app_obj.name}}】签名失败了
+
+
+ 签名时间:{{ now_time }}
+
+
+ 失败原因:开发者 {{developer_obj.issuer_id}} 状态 {developer_obj.get_status_display()}
+
+
+ 开发者备注:{{developer_obj.description}}。请登录后台查看具体失败信息
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/app_sign_over_limit.mjml.html b/mailhtml/src/app_sign_over_limit.mjml.html
new file mode 100644
index 0000000..ba79076
--- /dev/null
+++ b/mailhtml/src/app_sign_over_limit.mjml.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ 应用限额-签名失败通知
+
+
+
+
+
+
+
+
+
+ 应用【{{app_obj.name}}】签名失败了
+
+
+ 签名时间:{{ now_time }}
+
+
+ 失败原因:该应用已经使用设备数 {{used_num}},已超过您设置该应用的签名限额 {{limit_number}},当前已经无法安装新设备,为了避免业务使用,您可以修改该应用签名限额
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/apple_developer_cert_expired.mjml.html b/mailhtml/src/apple_developer_cert_expired.mjml.html
new file mode 100644
index 0000000..4b751b6
--- /dev/null
+++ b/mailhtml/src/apple_developer_cert_expired.mjml.html
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+ 苹果开发者证书即将到期通知
+
+
+
+
+
+
+
+
+
+ 你以下苹果开发者账号证书即将到期
+
+
+
+
+
+
+
+
+ 苹果开发者ID |
+ 苹果开发者备注 |
+ 证书到期时间 |
+
+
+ {% for developer_obj in developer_obj_list %}
+
+
+ {{ developer_obj.issuer_id }} |
+ {{ developer_obj.description }} |
+ {{ developer_obj.cert_expire_time }} |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/apple_developer_devices_over_limit.mjml.html b/mailhtml/src/apple_developer_devices_over_limit.mjml.html
new file mode 100644
index 0000000..72a0323
--- /dev/null
+++ b/mailhtml/src/apple_developer_devices_over_limit.mjml.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ 账户超级签名余额超过阈值通知
+
+
+
+
+
+
+
+
+
+ 您当前账户超级签名可用设备仅剩 {{device_count}}
+
+
+ 已超过您设置的阈值 {{user_obj.notify_available_signs}}
+
+
+ 为了避免业务使用,望您尽快添加苹果开发者!
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/apple_developer_unavailable.mjml.html b/mailhtml/src/apple_developer_unavailable.mjml.html
new file mode 100644
index 0000000..40dc436
--- /dev/null
+++ b/mailhtml/src/apple_developer_unavailable.mjml.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ 超级签名设备余额不足-签名失败通知
+
+
+
+
+
+
+
+
+
+ 应用【{{app_obj.name}}】签名失败了
+
+
+ 签名时间:{{ now_time }}
+
+
+ 失败原因:可用设备量已经不足或超限,请添加新的苹果开发者或修改开发者设备数量
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/base/footer.mjml b/mailhtml/src/base/footer.mjml
new file mode 100644
index 0000000..a13868e
--- /dev/null
+++ b/mailhtml/src/base/footer.mjml
@@ -0,0 +1,7 @@
+
+
+
+ FLY 应用分发平台
+
+
+
diff --git a/mailhtml/src/base/username.mjml b/mailhtml/src/base/username.mjml
new file mode 100644
index 0000000..e53cb8a
--- /dev/null
+++ b/mailhtml/src/base/username.mjml
@@ -0,0 +1,8 @@
+
+
+
+ 尊敬的用户{{
+ username }},您好:
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/change_userinfo.mjml.html b/mailhtml/src/change_userinfo.mjml.html
new file mode 100644
index 0000000..68b9972
--- /dev/null
+++ b/mailhtml/src/change_userinfo.mjml.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ 重要信息变更验证码
+
+
+
+
+
+
+
+
+ 您的验证码:【{{ code }}】
+
+
+ 操作时间:{{ now_time }}
+
+
+ 备注:您正在尝试变更重要信息,请妥善保管账户信息
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/code_notify.mjml.html b/mailhtml/src/code_notify.mjml.html
new file mode 100644
index 0000000..3e63aa9
--- /dev/null
+++ b/mailhtml/src/code_notify.mjml.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ 验证码通知
+
+
+
+
+
+
+
+
+ 您的验证码:【{{ code }}】
+
+
+ 操作时间:{{ now_time }}
+
+
+ 备注:若非本人操作,请忽略
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/download_times_not_enough.mjml.html b/mailhtml/src/download_times_not_enough.mjml.html
new file mode 100644
index 0000000..6f30929
--- /dev/null
+++ b/mailhtml/src/download_times_not_enough.mjml.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+ 账户下载余额不足通知
+
+
+
+
+
+
+
+
+
+ 您当前账户下载次数仅剩 {{user_obj.download_times}}
+
+
+ 您当前账户下载次数不足,应用已经无法下载安装。为了避免业务使用,望您尽快充值
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/download_times_over_limit.mjml.html b/mailhtml/src/download_times_over_limit.mjml.html
new file mode 100644
index 0000000..3293053
--- /dev/null
+++ b/mailhtml/src/download_times_over_limit.mjml.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ 账户下载余额超过阈值通知
+
+
+
+
+
+
+
+
+
+ 您当前账户下载次数仅剩 {{user_obj.download_times}}
+
+
+ 已超过您设置的阈值 {{user_obj.notify_available_downloads}}
+
+
+ 为了避免业务使用,望您尽快充值!
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/index.html b/mailhtml/src/index.html
new file mode 100644
index 0000000..8034673
--- /dev/null
+++ b/mailhtml/src/index.html
@@ -0,0 +1,265 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 尊敬的用户{{ username }},您好:
+
+ |
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 应用【{{app_obj.name}}】签名失败了
+
+ |
+
+
+
+
+ 签名时间:{{ now_time }}
+
+ |
+
+
+
+
+ 失败原因:该应用已经使用设备数 {{used_num}},已超过您设置该应用的签名限额
+ {{limit_number}},当前已经无法安装新设备,为了避免业务使用,您可以修改该应用签名限额
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mailhtml/src/index.mjml.html b/mailhtml/src/index.mjml.html
new file mode 100644
index 0000000..28b2940
--- /dev/null
+++ b/mailhtml/src/index.mjml.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+ 账户下载余额不足通知
+
+
+
+
+
+
+
+
+
+ 您当前账户下载次数仅剩 {{user_obj.download_times}}
+
+
+ 为了避免业务使用,望您尽快充值!
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/login_code.mjml.html b/mailhtml/src/login_code.mjml.html
new file mode 100644
index 0000000..ced6377
--- /dev/null
+++ b/mailhtml/src/login_code.mjml.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ 登录验证码
+
+
+
+
+
+
+
+
+ 您的验证码:【{{ code }}】
+
+
+ 操作时间:{{ now_time }}
+
+
+ 备注:您正在进行登录操作,若非本人操作,请勿泄露
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/pay_success.mjml.html b/mailhtml/src/pay_success.mjml.html
new file mode 100644
index 0000000..36366a8
--- /dev/null
+++ b/mailhtml/src/pay_success.mjml.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+ 充值到账通知
+
+
+
+
+
+
+
+
+
+ 您已经成功充值,订单信息如下
+
+
+ 订单号:{{order_obj.order_number}}
+
+
+ 支付时间:{{order_obj.pay_time}}
+
+
+ 支付方式:{{order_obj.get_payment_type_display}}
+
+
+ 订单信息:您充值了 {{order_obj.actual_download_times}} 下载次数,【赠送 {{order_obj.actual_download_gift_times}}】
+
+
+ 备注:{{order_obj.description}}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/register_code.mjml.html b/mailhtml/src/register_code.mjml.html
new file mode 100644
index 0000000..e3801c7
--- /dev/null
+++ b/mailhtml/src/register_code.mjml.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ 注册验证码
+
+
+
+
+
+
+
+
+ 您的验证码:【{{ code }}】
+
+
+ 操作时间:{{ now_time }}
+
+
+ 备注:您正在注册成为新用户,感谢您的支持
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/reset_password.mjml.html b/mailhtml/src/reset_password.mjml.html
new file mode 100644
index 0000000..3909ad2
--- /dev/null
+++ b/mailhtml/src/reset_password.mjml.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ 重置密码通知
+
+
+
+
+
+
+
+
+ 您的新密码:【{{ code }}】
+
+
+ 操作时间:{{ now_time }}
+
+
+ 备注:您的密码已经重置成功,请用新密码登录,并妥善保管账户信息
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mailhtml/src/timing_task_notify.mjml.html b/mailhtml/src/timing_task_notify.mjml.html
new file mode 100644
index 0000000..7db1dda
--- /dev/null
+++ b/mailhtml/src/timing_task_notify.mjml.html
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+ 苹果开发者定时任务报告
+
+
+
+
+
+
+
+
+
+
+ 设备数消耗统计
+
+
+ 当前正常设备总量:{{ developer_used_info.used_sign_number }}
+
+
+ 已使用:【平台:{{ developer_used_info.used_number }} 】【其他:{{ developer_used_info.can_other_used }}】
+
+
+
+ 还剩:{{ developer_used_info.can_sign_number }} 可用
+
+
+ 昨天消耗设备数:{{ yesterday_used_number }}
+
+
+
+
+
+
+
+ 定时任务检测已经执行完成
+
+
+
+
+
+
+
+ 苹果开发者ID |
+ 苹果开发者备注 |
+ 苹果开发者状态 |
+
+
+ {% for developer_obj in developer_obj_list %}
+
+
+ {{ developer_obj.issuer_id }} |
+ {{ developer_obj.description }} |
+ {{ developer_obj.get_status_display }} |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file