diff --git a/README.md b/README.md index 043cae0..4d9c036 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,10 @@ demo: https://qduoj.com TODO: + - 将判题服务器改为 rpc 通信 + - 重构消息队列 - 完善测试 - 完善小组功能 - - 后台重构 ![oj_previewindex.png][1] diff --git a/account/serializers.py b/account/serializers.py index 6db6291..0aac07a 100644 --- a/account/serializers.py +++ b/account/serializers.py @@ -50,7 +50,6 @@ class EditUserSerializer(serializers.Serializer): class ApplyResetPasswordSerializer(serializers.Serializer): - username = serializers.CharField(max_length=30) email = serializers.EmailField() captcha = serializers.CharField(max_length=4, min_length=4) diff --git a/account/views.py b/account/views.py index ac8ebdf..d6eb77d 100644 --- a/account/views.py +++ b/account/views.py @@ -14,7 +14,7 @@ from rest_framework.response import Response from utils.shortcuts import (serializer_invalid_response, error_response, success_response, error_page, paginate, rand_str) from utils.captcha import Captcha -from mail.tasks import send_email +from utils.mail import send_email from .decorators import login_required from .models import User, UserProfile @@ -62,10 +62,9 @@ def index_page(request): if not request.user.is_authenticated(): return render(request, "oj/index.html") - try: - if request.META['HTTP_REFERER']: + if request.META.get('HTTP_REFERER') or request.GET.get("index"): return render(request, "oj/index.html") - except KeyError: + else: return http.HttpResponseRedirect('/problems/') @@ -149,17 +148,27 @@ class UsernameCheckAPIView(APIView): class EmailCheckAPIView(APIView): def get(self, request): """ - 检测邮箱是否存在,存在返回状态码400,不存在返回200 + 检测邮箱是否存在,用状态码标识结果 --- """ + #这里是为了适应前端表单验证空间的要求 + reset = request.GET.get("reset", None) + #如果reset为true说明该请求是重置密码页面发出的,要返回的状态码应正好相反 + if reset: + existed = 200 + does_not_existed = 400 + else: + existed = 400 + does_not_existed = 200 + email = request.GET.get("email", None) if email: try: User.objects.get(email=email) - return Response(status=400) + return Response(status=existed) except Exception: - return Response(status=200) - return Response(status=200) + return Response(status=does_not_existed) + return Response(status=does_not_existed) class UserAdminAPIView(APIView): @@ -274,7 +283,7 @@ class ApplyResetPasswordAPIView(APIView): if not captcha.check(data["captcha"]): return error_response(u"验证码错误") try: - user = User.objects.get(username=data["username"], email=data["email"]) + user = User.objects.get(email=data["email"]) except User.DoesNotExist: return error_response(u"用户不存在") if user.reset_password_token_create_time and (now() - user.reset_password_token_create_time).total_seconds() < 20 * 60: @@ -286,14 +295,14 @@ class ApplyResetPasswordAPIView(APIView): email_template = email_template.replace("{{ username }}", user.username).\ replace("{{ website_name }}", settings.WEBSITE_INFO["website_name"]).\ - replace("{{ link }}", request.scheme + "://" + request.META['HTTP_HOST'] + "/reset_password/?token=" + user.reset_password_token) + replace("{{ link }}", request.scheme + "://" + request.META['HTTP_HOST'] + "/reset_password/t/" + user.reset_password_token) send_email(settings.WEBSITE_INFO["website_name"], user.email, user.username, - settings.WEBSITE_INFO["website_name"] + u" 密码找回邮件", + settings.WEBSITE_INFO["website_name"] + u" 登录信息找回邮件", email_template) - return success_response(u"邮件发送成功") + return success_response(u"邮件发送成功,请前往您的邮箱查收") else: return serializer_invalid_response(serializer) @@ -354,4 +363,14 @@ class SSOAPIView(APIView): token = rand_str() request.user.auth_token = token request.user.save() - return render(request, "oj/account/sso.html", {"redirect_url": callback + "?token=" + token, "callback": callback}) \ No newline at end of file + return render(request, "oj/account/sso.html", {"redirect_url": callback + "?token=" + token, "callback": callback}) + + +def reset_password_page(request, token): + try: + user = User.objects.get(reset_password_token=token) + except User.DoesNotExist: + return error_page(request, u"链接已失效") + if (now() - user.reset_password_token_create_time).total_seconds() > 30 * 60: + return error_page(request, u"链接已过期") + return render(request, "oj/account/reset_password.html", {"user": user}) diff --git a/contest/views.py b/contest/views.py index 7deb166..87378db 100644 --- a/contest/views.py +++ b/contest/views.py @@ -515,6 +515,10 @@ def contest_problem_submissions_list_page(request, contest_id, page=1): values("id", "contest_id", "problem_id", "result", "create_time", "accepted_answer_time", "language", "user_id").order_by("-create_time") + # 如果比赛已经开始,就不再显示之前测试题目的提交 + if contest.status != CONTEST_NOT_START: + submissions = submissions.filter(create_time__gte=contest.start_time) + user_id = request.GET.get("user_id", None) if user_id: submissions = submissions.filter(user_id=request.GET.get("user_id")) diff --git a/mail/__init__.py b/mail/__init__.py deleted file mode 100644 index 9bad579..0000000 --- a/mail/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# coding=utf-8 diff --git a/mail/celery.py b/mail/celery.py deleted file mode 100644 index 9bad579..0000000 --- a/mail/celery.py +++ /dev/null @@ -1 +0,0 @@ -# coding=utf-8 diff --git a/mail/tasks.py b/mail/tasks.py deleted file mode 100644 index 490472b..0000000 --- a/mail/tasks.py +++ /dev/null @@ -1,19 +0,0 @@ -# coding=utf-8 -import os -from envelopes import Envelope - -SMTP_CONFIG = {"smtp_server": "smtp.mxhichina.com", - "email": "noreply@qduoj.com", - "password": os.environ.get("smtp_password", "111111"), - "tls": False} - - -def send_email(from_name, to_email, to_name, subject, content): - envelope = Envelope(from_addr=(SMTP_CONFIG["email"], from_name), - to_addr=(to_email, to_name), - subject=subject, - html_body=content) - envelope.send(SMTP_CONFIG["smtp_server"], - login=SMTP_CONFIG["email"], - password=SMTP_CONFIG["password"], - tls=SMTP_CONFIG["tls"]) diff --git a/oj/settings.py b/oj/settings.py index 153c1af..1d3cc37 100644 --- a/oj/settings.py +++ b/oj/settings.py @@ -55,7 +55,6 @@ INSTALLED_APPS = ( 'admin', 'submission', 'contest', - 'mail', 'judge', 'judge_dispatcher', @@ -125,7 +124,6 @@ AUTH_USER_MODEL = 'account.User' LOG_PATH = "log/" - LOGGING = { 'version': 1, 'disable_existing_loggers': True, @@ -191,9 +189,10 @@ TEST_CASE_DIR = os.path.join(BASE_DIR, 'test_case/') IMAGE_UPLOAD_DIR = os.path.join(BASE_DIR, 'upload/') WEBSITE_INFO = {"website_name": "qduoj", - "website_footer": u"青岛大学信息工程学院 创新实验室", + "website_footer": u"青岛大学信息工程学院 创新实验室 京ICP备15062075号-1", "url": "https://qduoj.com"} + HUEY = { 'backend': 'huey.backends.redis_backend', 'name': 'task_queue', @@ -201,4 +200,11 @@ HUEY = { 'always_eager': False, # Defaults to False when running via manage.py run_huey # Options to pass into the consumer when running ``manage.py run_huey`` 'consumer_options': {'workers': 50}, -} \ No newline at end of file +} + + +SMTP_CONFIG = {"smtp_server": "smtp.mxhichina.com", + "email": "noreply@qduoj.com", + "password": os.environ.get("smtp_password", "111111"), + "tls": False} + diff --git a/oj/urls.py b/oj/urls.py index 6ba6b06..d2beba1 100644 --- a/oj/urls.py +++ b/oj/urls.py @@ -5,7 +5,7 @@ from django.views.generic import TemplateView from account.views import (UserLoginAPIView, UsernameCheckAPIView, UserRegisterAPIView, UserChangePasswordAPIView, EmailCheckAPIView, - UserAdminAPIView, UserInfoAPIView, + UserAdminAPIView, UserInfoAPIView, ResetPasswordAPIView, ApplyResetPasswordAPIView, SSOAPIView, UserProfileAPIView) from announcement.views import AnnouncementAdminAPIView @@ -122,12 +122,14 @@ urlpatterns = [ url(r'^user/(?P.+)/$', "account.views.user_index_page"), - url(r'^api/reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"), - + url(r'^api/apply_reset_password/$', ApplyResetPasswordAPIView.as_view(), name="apply_reset_password_api"), + url(r'^api/reset_password/$', ResetPasswordAPIView.as_view(), name="apply_reset_password_api"), url(r'^account/settings/$', TemplateView.as_view(template_name="oj/account/settings.html"), name="account_setting_page"), url(r'^account/settings/avatar/$', TemplateView.as_view(template_name="oj/account/avatar.html"), name="avatar_settings_page"), url(r'^account/sso/$', SSOAPIView.as_view(), name="sso_api"), - url('^api/account/userprofile/$', UserProfileAPIView.as_view(), name="userprofile_api"), + url(r'^api/account/userprofile/$', UserProfileAPIView.as_view(), name="userprofile_api"), + url(r'^reset_password/$', TemplateView.as_view(template_name="oj/account/apply_reset_password.html"), name="apply_reset_password_page"), + url(r'^reset_password/t/(?P\w+)/$', "account.views.reset_password_page", name="reset_password_page") ] diff --git a/static/src/img/index/bg/bg0.jpg b/static/src/img/index/bg/bg0.jpg new file mode 100644 index 0000000..02c9417 Binary files /dev/null and b/static/src/img/index/bg/bg0.jpg differ diff --git a/static/src/img/index/bg/bg1.jpg b/static/src/img/index/bg/bg1.jpg new file mode 100644 index 0000000..4d4862f Binary files /dev/null and b/static/src/img/index/bg/bg1.jpg differ diff --git a/static/src/img/index/bg/bg2.jpg b/static/src/img/index/bg/bg2.jpg new file mode 100644 index 0000000..aa80557 Binary files /dev/null and b/static/src/img/index/bg/bg2.jpg differ diff --git a/static/src/img/index/bg/bg3.jpg b/static/src/img/index/bg/bg3.jpg new file mode 100644 index 0000000..5c4b58f Binary files /dev/null and b/static/src/img/index/bg/bg3.jpg differ diff --git a/static/src/js/app/oj/account/applyResetPassword.js b/static/src/js/app/oj/account/applyResetPassword.js new file mode 100644 index 0000000..e8d31ed --- /dev/null +++ b/static/src/js/app/oj/account/applyResetPassword.js @@ -0,0 +1,39 @@ +require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) { + var applied_captcha = false; + $('form').validator().on('submit', function (e) { + if (!e.isDefaultPrevented()) { + var email = $("#email").val(); + var captcha = $("#captcha").val(); + + $.ajax({ + beforeSend: csrfTokenHeader, + url: "/api/apply_reset_password/", + data: {email: email, captcha: captcha}, + dataType: "json", + method: "post", + success: function (data) { + if (!data.code) { + refresh_captcha(); + bsAlert(data.data); + } + else { + refresh_captcha(); + bsAlert(data.data); + } + }, + error: function(){ + bsAlert("额 好像出错了,请刷新页面重试。如还有问题,请填写页面导航栏上的反馈。") + } + + }); + return false; + } + }); + function refresh_captcha(){ + $("#captcha-img")[0].src = "/captcha/?" + Math.random(); + $("#captcha")[0].value = ""; + } + $("#captcha-img").click(function(){ + refresh_captcha(); + }); +}); \ No newline at end of file diff --git a/static/src/js/app/oj/account/resetPassword.js b/static/src/js/app/oj/account/resetPassword.js new file mode 100644 index 0000000..0f0bb4b --- /dev/null +++ b/static/src/js/app/oj/account/resetPassword.js @@ -0,0 +1,41 @@ +require(["jquery", "bsAlert", "csrfToken", "validator"], function ($, bsAlert, csrfTokenHeader) { + var applied_captcha = false; + $('form').validator().on('submit', function (e) { + if (!e.isDefaultPrevented()) { + var index = location.href.indexOf("/t/"); + var token = location.href.substr(36+3, 32); + var captcha = $("#captcha").val(); + var password = $("#new_password").val(); + $.ajax({ + beforeSend: csrfTokenHeader, + url: "/api/reset_password/", + data: {password: password, captcha: captcha, token:token}, + dataType: "json", + method: "post", + success: function (data) { + if (!data.code) { + refresh_captcha(); + bsAlert(data.data); + window.location.href = "/login/"; + } + else { + refresh_captcha(); + bsAlert(data.data); + } + }, + error: function(){ + bsAlert("额 好像出错了,请刷新页面重试。如还有问题,请填写页面导航栏上的反馈。") + } + + }); + return false; + } + }); + function refresh_captcha(){ + $("#captcha-img")[0].src = "/captcha/?" + Math.random(); + $("#captcha")[0].value = ""; + } + $("#captcha-img").click(function(){ + refresh_captcha(); + }); +}); \ No newline at end of file diff --git a/static/src/js/build.js b/static/src/js/build.js index beb891e..8c8de62 100644 --- a/static/src/js/build.js +++ b/static/src/js/build.js @@ -57,25 +57,28 @@ problem_2_pack: "app/oj/problem/problem", submissionList_3_pack: "app/admin/problem/submissionList", contestCountdown_4_pack: "app/oj/contest/contestCountdown", - addProblem_5_pack: "app/admin/problem/addProblem", - problem_6_pack: "app/admin/problem/problem", - contestList_7_pack: "app/admin/contest/contestList", - admin_8_pack: "app/admin/admin", - login_9_pack: "app/oj/account/login", - addContest_10_pack: "app/admin/contest/addContest", - contestPassword_11_pack: "app/oj/contest/contestPassword", - changePassword_12_pack: "app/oj/account/changePassword", - monitor_13_pack: "app/admin/monitor/monitor", - editProblem_14_pack: "app/admin/contest/editProblem", - joinGroupRequestList_15_pack: "app/admin/group/joinGroupRequestList", - group_16_pack: "app/oj/group/group", - contestProblemList_17_pack: "app/admin/contest/contestProblemList", - editProblem_18_pack: "app/admin/problem/editProblem", - register_19_pack: "app/oj/account/register", - groupDetail_20_pack: "app/admin/group/groupDetail", - editContest_21_pack: "app/admin/contest/editContest", - group_22_pack: "app/admin/group/group", - settings_23_pack: "app/oj/account/settings" + avatar_5_pack: "app/oj/account/avatar", + addProblem_6_pack: "app/admin/problem/addProblem", + problem_7_pack: "app/admin/problem/problem", + contestList_8_pack: "app/admin/contest/contestList", + admin_9_pack: "app/admin/admin", + login_10_pack: "app/oj/account/login", + applyResetPassword_11_pack: "app/oj/account/applyResetPassword", + addContest_12_pack: "app/admin/contest/addContest", + contestPassword_13_pack: "app/oj/contest/contestPassword", + changePassword_14_pack: "app/oj/account/changePassword", + monitor_15_pack: "app/admin/monitor/monitor", + editProblem_16_pack: "app/admin/contest/editProblem", + joinGroupRequestList_17_pack: "app/admin/group/joinGroupRequestList", + group_18_pack: "app/oj/group/group", + contestProblemList_19_pack: "app/admin/contest/contestProblemList", + editProblem_20_pack: "app/admin/problem/editProblem", + register_21_pack: "app/oj/account/register", + groupDetail_22_pack: "app/admin/group/groupDetail", + editContest_23_pack: "app/admin/contest/editContest", + resetPassword_24_pack: "app/oj/account/resetPassword", + group_25_pack: "app/admin/group/group", + settings_26_pack: "app/oj/account/settings" }, shim: { avalon: { @@ -86,12 +89,6 @@ appDir: "../", dir: "../../release/", modules: [ - { - name: "bootstrap", - }, - { - name: "codeMirror" - }, { name: "announcement_0_pack" }, @@ -108,62 +105,71 @@ name: "contestCountdown_4_pack" }, { - name: "addProblem_5_pack" + name: "avatar_5_pack" }, { - name: "problem_6_pack" + name: "addProblem_6_pack" }, { - name: "contestList_7_pack" + name: "problem_7_pack" }, { - name: "admin_8_pack" + name: "contestList_8_pack" }, { - name: "login_9_pack" + name: "admin_9_pack" }, { - name: "addContest_10_pack" + name: "login_10_pack" }, { - name: "contestPassword_11_pack" + name: "applyResetPassword_11_pack" }, { - name: "changePassword_12_pack" + name: "addContest_12_pack" }, { - name: "monitor_13_pack" + name: "contestPassword_13_pack" }, { - name: "editProblem_14_pack" + name: "changePassword_14_pack" }, { - name: "joinGroupRequestList_15_pack" + name: "monitor_15_pack" }, { - name: "group_16_pack" + name: "editProblem_16_pack" }, { - name: "contestProblemList_17_pack" + name: "joinGroupRequestList_17_pack" }, { - name: "editProblem_18_pack" + name: "group_18_pack" }, { - name: "register_19_pack" + name: "contestProblemList_19_pack" }, { - name: "groupDetail_20_pack" + name: "editProblem_20_pack" }, { - name: "editContest_21_pack" + name: "register_21_pack" }, { - name: "group_22_pack" + name: "groupDetail_22_pack" }, { - name: "settings_23_pack" + name: "editContest_23_pack" }, + { + name: "resetPassword_24_pack" + }, + { + name: "group_25_pack" + }, + { + name: "settings_26_pack" + } ], optimizeCss: "standard", }) \ No newline at end of file diff --git a/static/src/js/config.js b/static/src/js/config.js index e2856f6..ee6f52b 100644 --- a/static/src/js/config.js +++ b/static/src/js/config.js @@ -59,25 +59,28 @@ var require = { problem_2_pack: "app/oj/problem/problem", submissionList_3_pack: "app/admin/problem/submissionList", contestCountdown_4_pack: "app/oj/contest/contestCountdown", - addProblem_5_pack: "app/admin/problem/addProblem", - problem_6_pack: "app/admin/problem/problem", - contestList_7_pack: "app/admin/contest/contestList", - admin_8_pack: "app/admin/admin", - login_9_pack: "app/oj/account/login", - addContest_10_pack: "app/admin/contest/addContest", - contestPassword_11_pack: "app/oj/contest/contestPassword", - changePassword_12_pack: "app/oj/account/changePassword", - monitor_13_pack: "app/admin/monitor/monitor", - editProblem_14_pack: "app/admin/contest/editProblem", - joinGroupRequestList_15_pack: "app/admin/group/joinGroupRequestList", - group_16_pack: "app/oj/group/group", - contestProblemList_17_pack: "app/admin/contest/contestProblemList", - editProblem_18_pack: "app/admin/problem/editProblem", - register_19_pack: "app/oj/account/register", - groupDetail_20_pack: "app/admin/group/groupDetail", - editContest_21_pack: "app/admin/contest/editContest", - group_22_pack: "app/admin/group/group", - settings_23_pack: "app/oj/account/settings" + avatar_5_pack: "app/oj/account/avatar", + addProblem_6_pack: "app/admin/problem/addProblem", + problem_7_pack: "app/admin/problem/problem", + contestList_8_pack: "app/admin/contest/contestList", + admin_9_pack: "app/admin/admin", + login_10_pack: "app/oj/account/login", + applyResetPassword_11_pack: "app/oj/account/applyResetPassword", + addContest_12_pack: "app/admin/contest/addContest", + contestPassword_13_pack: "app/oj/contest/contestPassword", + changePassword_14_pack: "app/oj/account/changePassword", + monitor_15_pack: "app/admin/monitor/monitor", + editProblem_16_pack: "app/admin/contest/editProblem", + joinGroupRequestList_17_pack: "app/admin/group/joinGroupRequestList", + group_18_pack: "app/oj/group/group", + contestProblemList_19_pack: "app/admin/contest/contestProblemList", + editProblem_20_pack: "app/admin/problem/editProblem", + register_21_pack: "app/oj/account/register", + groupDetail_22_pack: "app/admin/group/groupDetail", + editContest_23_pack: "app/admin/contest/editContest", + resetPassword_24_pack: "app/oj/account/resetPassword", + group_25_pack: "app/admin/group/group", + settings_26_pack: "app/oj/account/settings", }, shim: { avalon: { diff --git a/template/src/admin/contest/add_contest.html b/template/src/admin/contest/add_contest.html index ce8fd14..010b73e 100644 --- a/template/src/admin/contest/add_contest.html +++ b/template/src/admin/contest/add_contest.html @@ -1,6 +1,9 @@
+
diff --git a/template/src/admin/contest/edit_contest.html b/template/src/admin/contest/edit_contest.html index 2afc849..08f7c09 100644 --- a/template/src/admin/contest/edit_contest.html +++ b/template/src/admin/contest/edit_contest.html @@ -6,6 +6,9 @@ aria-hidden="true">← 返回 +
diff --git a/template/src/oj/account/apply_reset_password.html b/template/src/oj/account/apply_reset_password.html new file mode 100644 index 0000000..5d65ccc --- /dev/null +++ b/template/src/oj/account/apply_reset_password.html @@ -0,0 +1,40 @@ +{% extends "oj_base.html" %} +{% block title %} + 找回登录信息 +{% endblock %} +{% block body %} +
+
+

找回登录信息


+
+

请输入你注册时使用的邮箱地址,系统将自动向你的邮箱发送一封含有您登录信息的电子邮件, + 你可以看到你的用户名,并可以选择重新设置登录密码,注意为了你的账户安全,重置密码链接仅在30分钟内有效

+
+
+ +
+ + + +
+
+
+    +

+ + +
+
+
+ +
+ +
+
+{% endblock %} +{% block js_block %} + +{% endblock %} \ No newline at end of file diff --git a/template/src/oj/account/change_password.html b/template/src/oj/account/change_password.html index 3f346e2..9fd1334 100644 --- a/template/src/oj/account/change_password.html +++ b/template/src/oj/account/change_password.html @@ -36,7 +36,7 @@
+ placeholder="确认密码" maxlength="30" data-match="#new_password" data-match-error="两次密码不一致" required>
diff --git a/template/src/oj/account/login.html b/template/src/oj/account/login.html index adc5ca9..eadb5cc 100644 --- a/template/src/oj/account/login.html +++ b/template/src/oj/account/login.html @@ -32,6 +32,7 @@
+ 忘记用户名/密码
还没有帐号?点击注册 diff --git a/template/src/oj/account/reset_password.html b/template/src/oj/account/reset_password.html index 71ce692..703ff6f 100644 --- a/template/src/oj/account/reset_password.html +++ b/template/src/oj/account/reset_password.html @@ -1,10 +1,53 @@ - - - - - - - +{% extends "oj_base.html" %} +{% block title %} + 找回登录信息 +{% endblock %} +{% block body %} +
+
+

找回登录信息


+
- - \ No newline at end of file +
+
+ + +
+
+ + +
+
+ + + +
+
+
+ + + +
+
+
+    +

+ + +
+
+
+ +
+
+
+
+{% endblock %} +{% block js_block %} + +{% endblock %} \ No newline at end of file diff --git a/template/src/oj/index.html b/template/src/oj/index.html index 4a8edd3..0245084 100644 --- a/template/src/oj/index.html +++ b/template/src/oj/index.html @@ -100,6 +100,15 @@ max-width: 300px; } + .section{ + background-size: cover; + } + + #section0{ + background-image: url(/static/img/index/bg/bg3.jpg); + padding: 0 0 0 0; + } + @@ -110,12 +119,21 @@ @@ -172,7 +190,7 @@
-

自由举办小组赛(10月上线)

+

自由举办小组赛(12月上线)

内部比赛,日常作业,期末考试,通通搞定

diff --git a/template/src/utils/reset_password_email.html b/template/src/utils/reset_password_email.html index f4c8b93..5a0b591 100644 --- a/template/src/utils/reset_password_email.html +++ b/template/src/utils/reset_password_email.html @@ -8,7 +8,7 @@ - {{ website_name }} 密码找回邮件 + {{ website_name }} 登录信息找回 @@ -32,12 +32,12 @@ - 您刚刚在 {{ website_name }} 使用了找回密码功能。 + 您刚刚在 {{ website_name }} 申请了找回登录信息服务。 - 请在60分钟内点击下面链接设置您的新密码: + 请在30分钟内点击下面链接设置您的新密码: @@ -63,7 +63,8 @@ - 如果你没有提出过密码修改申请,请忽略此邮件。有可能是其他用户误填了你的用户名。我们不会对你的帐户进行任何修改。 + 如果您没有提出过该申请,请忽略此邮件。有可能是其他用户误填了您的邮件地址,我们不会对你的帐户进行任何修改。 + 请不要向他人透露本邮件的内容,否则可能会导致您的账号被盗。 diff --git a/utils/mail.py b/utils/mail.py new file mode 100644 index 0000000..5d4e1c3 --- /dev/null +++ b/utils/mail.py @@ -0,0 +1,15 @@ +# coding=utf-8 +from envelopes import Envelope + +from django.conf import settings + + +def send_email(from_name, to_email, to_name, subject, content): + envelope = Envelope(from_addr=(settings.SMTP_CONFIG["email"], from_name), + to_addr=(to_email, to_name), + subject=subject, + html_body=content) + envelope.send(settings.SMTP_CONFIG["smtp_server"], + login=settings.SMTP_CONFIG["email"], + password=settings.SMTP_CONFIG["password"], + tls=settings.SMTP_CONFIG["tls"])