Compare commits

...

2 Commits

Author SHA1 Message Date
cxykevin fd8564ecc2 添加重置按钮 2024-08-20 21:49:19 +08:00
cxykevin 08b822fff8 完成重置密码 2024-08-20 21:37:47 +08:00
4 changed files with 136 additions and 9 deletions

View File

@ -1,7 +1,7 @@
from fastapi.security import OAuth2PasswordBearer from fastapi.security import OAuth2PasswordBearer
from fastapi import FastAPI, Cookie, Response, Form from fastapi import FastAPI, Cookie, Response, Form
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from fastapi.responses import RedirectResponse from fastapi.responses import RedirectResponse, HTMLResponse
from datetime import timedelta, datetime from datetime import timedelta, datetime
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from . import db from . import db
@ -72,7 +72,7 @@ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def check_passwd(passwd: str): def check_passwd(passwd: str):
if (len(passwd) < 8): if (len(passwd) < 8):
return 1 return 1
pattern = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$' pattern = r'^(?![a-zA-Z]+$)(?!\d+$)(?![^\da-zA-Z\s]+$).{8,40}$'
if re.match(pattern, passwd): if re.match(pattern, passwd):
return 0 return 0
@ -150,12 +150,14 @@ async def login_callback(username: str = Form(), password: str = Form(), email:
@app.get("/api/checkemail") @app.get("/api/checkemail")
async def login_callback(uid: str): async def checkemail(uid: str):
if (uid not in emails): if (uid not in emails):
return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "不存在的注册id"}) return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "不存在的注册id"})
if (emails[uid][2] < datetime.now()): if (emails[uid][2] < datetime.now()):
del emails[uid] del emails[uid]
return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "链接已过期"}) return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "链接已过期"})
if (emails[uid][1] == ""):
return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "不存在的注册id"})
if await db.create_user(emails[uid][0], emails[uid][1], emails[uid][3]) == 0: if await db.create_user(emails[uid][0], emails[uid][1], emails[uid][3]) == 0:
del emails[uid] del emails[uid]
return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "创建成功"}) return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "创建成功"})
@ -164,6 +166,36 @@ async def login_callback(uid: str):
return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "重复的用户名"}) return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "重复的用户名"})
@app.get("/api/resetpasswd", response_class=HTMLResponse)
async def resetpasswd(uid: str, response: Response):
if (uid not in emails):
return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "不存在的验证id"})
if (emails[uid][2] < datetime.now()):
del emails[uid]
return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "链接已过期"})
if (emails[uid][1] != ""):
return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "不存在的注册id"})
tokennow = await create_token(emails[uid][0])
tkn = uuid.uuid4().hex
apikeys[tkn] = tokens[tokennow]
response.set_cookie("session", tokennow)
return '<html><head><meta http-equiv="refresh" content="0;url=/user"><title>正在跳转</title></head><body>正在跳转</body></html>'
@app.post("/api/send_resetpasswd")
async def resetpasswd(username: str = Form()):
if (await db.check_user(username)):
email = await db.get_email(username)
tkn = uuid.uuid4().hex
emails[tkn] = (username, "", datetime.now() +
timedelta(minutes=float(ACCESS_EMAIL_EXPIRE_MINUTES)), email)
email_send_lst.append((email, ROOT+"/api/resetpasswd?uid="+tkn))
return {"msg": "验证邮件已发送到邮箱请在10分钟内完成验证", "code": 0}
else:
return {"msg": "用户名不存在", "code": 1}
@app.get("/api/getinfo") @app.get("/api/getinfo")
async def get_user_info(uid: str): async def get_user_info(uid: str):
username = await check_apikey(uid) username = await check_apikey(uid)
@ -219,6 +251,11 @@ async def login(session: Annotated[str | None, Cookie()] = None):
return templates.TemplateResponse("manage.html", {"request": {}}) return templates.TemplateResponse("manage.html", {"request": {}})
@app.get("/resetpasswd")
async def resetpasswd(response: Response):
return templates.TemplateResponse("resetpasswd.html", {"request": {}})
@app.get("/manager/init") @app.get("/manager/init")
async def init(key: str): async def init(key: str):
if (key != MANAGE_KEY): if (key != MANAGE_KEY):

View File

@ -27,12 +27,13 @@ def main(app: FastAPI, ROOT: str, apikeys: dict):
return Response(""" return Response("""
setTimeout(function() { setTimeout(function() {
var signup_obj = document.getElementsByClassName( var signup_obj = document.getElementsByClassName(
"item-signUp")[0] "item-logIn")[0]
if(signup_obj!=undefined){ if(signup_obj!=undefined){
signup_obj.style.display = "none"; signup_obj.style.display = "none";
} }
document.getElementsByClassName( var btn_obj=document.getElementsByClassName("item-logIn")[0]
"item-logIn")[0].innerHTML = "<a href='"""+ROOT+"""/login?redirect_url="+window.location.href+"'>登录/注册</a>"; if(btn_obj != undefined){
btn_obj.innerHTML = "<a href='"""+ROOT+"""/login?redirect_url="+window.location.href+"'>注册/登录</a>"}
}, 500); }, 500);
@ -45,17 +46,18 @@ def main(app: FastAPI, ROOT: str, apikeys: dict):
var key = res[0] var key = res[0]
var xhr = new XMLHttpRequest() var xhr = new XMLHttpRequest()
var csrf = JSON.parse(document.getElementById("flarum-json-payload").innerText)["session"]["csrfToken"] var csrf = JSON.parse(document.getElementById("flarum-json-payload").innerText)["session"]["csrfToken"]
xhr.open('post','"""+FL_SERVER+"""/app/flarum/login?apikey='+key+'&csrftoken='+csrf); xhr.open('post','/app/flarum/login?apikey='+key+'&csrftoken='+csrf);
xhr.onreadyStatechange = function () { xhr.onreadyStatechange = function () {
if(xhr.readyState === 4 && xhr.status === 200) { if(xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText) console.log(xhr.responseText)
window.location.hash = ""
location.reload()
} }
} }
xhr.send() xhr.send()
} }
} }
}, 1000); }, 1000);
""", 200, None, media_type="application/javascript") """, 200, None, media_type="application/javascript")
@app.post("/app/flarum/login") @app.post("/app/flarum/login")

View File

@ -48,6 +48,8 @@
label="用户名"></mdui-text-field> label="用户名"></mdui-text-field>
<mdui-text-field id="passwd" style="display:block;height:54px;width:90%;left:10%;margin:auto" <mdui-text-field id="passwd" style="display:block;height:54px;width:90%;left:10%;margin:auto"
label="密码" type="password" toggle-password></mdui-text-field> label="密码" type="password" toggle-password></mdui-text-field>
<mdui-button style="width:90%;margin-left:5%" variant="elevated"
href="/resetpasswd">忘记密码?点我重置</mdui-button>
<div style="position: absolute;bottom:20px;width:100%"> <div style="position: absolute;bottom:20px;width:100%">
<mdui-button id="loginbtn" style="width:calc(90% - 110px);margin-right:10px;margin-left:5%" <mdui-button id="loginbtn" style="width:calc(90% - 110px);margin-right:10px;margin-left:5%"
onclick="logins()">登录</mdui-button> onclick="logins()">登录</mdui-button>

86
src/resetpasswd.html Normal file
View File

@ -0,0 +1,86 @@
<html class="mdui-theme-auto">
<head>
<link rel="stylesheet" href="https://learn.study-area.org.cn/theme/css/mdui.css">
<script src="https://learn.study-area.org.cn/theme/js/mdui.global.js" type="text/javascript"></script>
<link href="https://learn.study-area.org.cn/theme/css/icons.css" rel="stylesheet">
<title>StuyAreaCN Accout System</title>
<style>
* {
font-family: Arial, Helvetica, sans-serif;
}
</style>
</head>
<body style="margin:0;padding:0;">
<mdui-dialog id="dialog" close-on-overlay-click>
</mdui-dialog>
<mdui-layout style="height: 100%">
<mdui-top-app-bar>
<mdui-top-app-bar-title>StudyAreaCN</mdui-top-app-bar-title>
</mdui-top-app-bar>
<mdui-navigation-drawer class="navi-drawer" id="toc-drawer">
<mdui-list>
<mdui-list-item end-icon="arrow_right" href="https://learn.study-area.org.cn"
rounded>开始学习</mdui-list-item>
<mdui-list-item end-icon="arrow_right" href="https://forum.study-area.org.cn"
rounded>讨论区</mdui-list-item>
<mdui-list-item end-icon="arrow_right"
href="https://git.hmtsai.cn/study-area-cn/study-area-cn-homepage/" rounded>查看本站源码</mdui-list-item>
</mdui-list>
</mdui-navigation-drawer>
<mdui-layout-main style="height: 100%">
<div style="width: 100%;height: 100%">
<mdui-card
style="width:30%;min-width:400px;height:60%;min-height:500px;margin: auto;display: block;transform: translate(-50%,-50%);left: 50%;top: 50%;position: absolute;"
id="cards">
<div class="header"
style="font-size: 38px;font-weight: bold;text-align: center;line-height: 160px;">
重置密码
</div>
<mdui-text-field id="username"
style="display:block;height:54px;width:90%;margin:auto;margin-bottom: 16px"
label="用户名"></mdui-text-field>
<div style="position: absolute;bottom:20px;width:100%">
<mdui-button id="loginbtn" style="width:90%;margin-right:10px;margin-left:5%"
onclick="logins()">发送邮件</mdui-button>
</div>
</mdui-card>
</div>
</mdui-layout-main>
</mdui-layout>
<script>
if (window.innerWidth > 800) {
document.getElementById("cards").style.transform = "translateY(-50%)"
document.getElementById("toc-drawer").setAttribute('open', true);
}
function logins() {
document.getElementById("loginbtn").setAttribute('loading', "");
const Http = new XMLHttpRequest();
const url = '/api/send_resetpasswd';
Http.open("POST", url);
var formData = new FormData();
formData.append('username', document.getElementById("username").value);
Http.send(formData);
Http.addEventListener('loadend', () => {
var ret = JSON.parse(Http.responseText)
if (ret.msg == "") {
const searchParams = new URLSearchParams(window.location.search);
window.location.href = "/login/?redirect_url=/user"
} else {
document.getElementById("dialog").innerHTML = ret.msg
document.getElementById("dialog").setAttribute("open", "")
}
document.getElementById("loginbtn").removeAttribute('loading');
})
}
</script>
</body>
</html>