diff --git a/README.md b/README.md index ac3c228..e06d56b 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,6 @@ maxsize = 100 最大连接数 minsize = 10 最小连接数 [common] -algorithm = "HS256" 使用的加密算法 access_token_expire_minutes = 60 token过期时长(min) access_email_expire_minutes = 10 注册邮件过期时长(min) root = !这个帐号服务的域名,需要有 http(s):// @@ -68,6 +67,25 @@ port = 465 邮件SMTP端口 addr = !邮箱 passwd = !邮箱的授权码(部分情况就是密码,部分情况需要申请) +[ui] +prod_name = !显示的名称 +title = !网页标题 +manager_email = !管理员邮箱 +bar_items = [ + [ + "开始学习", + "https://learn.study-area.org.cn", + ], + [ + "讨论区", + "https://forum.study-area.org.cn", + ], + [ + "查看本站源码", + "https://git.hmtsai.cn/study-area-cn/study-area-cn-homepage/", + ], +] 电脑端左侧栏显示内容 + ``` ## 内置扩展 diff --git a/config/config.sample.toml b/config/config.sample.toml index 7cb0238..13e11e5 100644 --- a/config/config.sample.toml +++ b/config/config.sample.toml @@ -1,3 +1,12 @@ +[common] +lang = "zh_cn" +access_token_expire_minutes = 60 +access_email_expire_minutes = 1 +root = "http://127.0.0.1:8000" +clean_timeout = 3600 +manage_key = "some_random_key_for_init" +redirect_url_whitelist = ["/user", "/login", "/signup", "127.0.0.1:8000"] + [db] host = "192.168.10.119" port = 3306 @@ -7,21 +16,32 @@ db = "sacn_accout" maxsize = 100 minsize = 10 -[common] -algorithm = "HS256" -access_token_expire_minutes = 60 -access_email_expire_minutes = 1 -root = "http://127.0.0.1:8000" -clean_timeout = 3600 -manage_key = "some_random_key_for_init" -redirect_url_whitelist = ["/user", "/login", "/signup", "127.0.0.1:8000"] - -[flarum] -fl_server = "http://127.0.0.1:4000" -fl_apikey = "some_long_key" - [email] smtp_srv = "smtp.qq.com" port = 465 addr = "you_email" passwd = "you_email_access_code" + +[ui] +prod_name = "StudyAreaCN" +title = "StuyAreaCN Account System" +manager_email = "" +bar_items = [ + [ + "开始学习", + "https://learn.study-area.org.cn", + ], + [ + "讨论区", + "https://forum.study-area.org.cn", + ], + [ + "查看本站源码", + "https://git.hmtsai.cn/study-area-cn/study-area-cn-homepage/", + ], +] + + +[flarum] +fl_server = "http://127.0.0.1:4000" +fl_apikey = "some_long_key" diff --git a/lang/zh_cn.toml b/lang/zh_cn.toml new file mode 100644 index 0000000..a0dd540 --- /dev/null +++ b/lang/zh_cn.toml @@ -0,0 +1,26 @@ +redirect = "正在跳转" +login = "登录" +signup = "注册" +username = "用户名" +password = "密码" +email = "邮箱" +reset_passwd = "重置密码" +check_passwd = "确认密码" +change_passwd = "更改密码" +passwd_not_same = "密码不一致" +change_info = "若需要注销或修改账户的邮箱,请发送邮件至" +forgot_password_info = "忘记密码?点我重置" +manage = "用户管理" +send_email = "发送邮件" +unedit_msg = "内容为空" +username_or_password_incorrect = "用户名或密码错误" +weak_passwd = "密码强度弱,请至少包含一个大小写字符与数字且长度>8" +invalid_email = "邮箱不合法" +verification_email = "验证邮件已发送到邮箱,请在{}分钟内完成验证" +username_exists = "用户名重复" +username_not_exists = "用户名不存在" +invalid_regid = "不存在的注册id" +invalid_checkid = "不存在的验证id" +created_successfully = "创建成功" +invalid_token = "token无效" +invalid_redirect_url = "无效的重定向URL" diff --git a/server/cfg.py b/server/cfg.py index ad32670..c3cb379 100644 --- a/server/cfg.py +++ b/server/cfg.py @@ -1,9 +1,13 @@ import tomllib import os + with open("config/config.toml", "rb") as f: config = tomllib.load(f) +with open(f"lang/{config["common"]["lang"]}.toml", "rb") as f: + lang = tomllib.load(f) + def reload(): global config diff --git a/server/main.py b/server/main.py index 28a10d5..885568c 100644 --- a/server/main.py +++ b/server/main.py @@ -21,7 +21,6 @@ import importlib def load_cfg(): - global ALGORITHM global ACCESS_TOKEN_EXPIRE_MINUTES global ACCESS_EMAIL_EXPIRE_MINUTES global ROOT @@ -29,7 +28,6 @@ def load_cfg(): global MANAGE_KEY global REDIRECT_URL_WHITELIST - ALGORITHM = cfg.config["common"]["algorithm"] ACCESS_TOKEN_EXPIRE_MINUTES = cfg.config["common"]["access_token_expire_minutes"] ACCESS_EMAIL_EXPIRE_MINUTES = cfg.config["common"]["access_email_expire_minutes"] ROOT = cfg.config["common"]["root"] @@ -148,7 +146,7 @@ async def login_callback(response: Response, username: str = Form(), password: s response.set_cookie("session", clean_uuid(tokennow)) return {"msg": "", "key": clean_uuid(tkn)} else: - return {"msg": "用户名或密码错误", "key": ""} + return {"msg": cfg.lang["username_or_password_incorrect"], "key": ""} regex = re.compile( @@ -158,9 +156,9 @@ regex = re.compile( @app.post("/api/signup") async def login_callback(username: str = Form(), password: str = Form(), email: str = Form()): if (check_passwd(password)): - return {"msg": "密码强度弱,请至少包含一个大小写字符与数字且长度>8", "code": 1} + return {"msg": cfg.lang["weak_passwd"], "code": 1} if (not re.fullmatch(regex, email)): - return {"msg": "邮箱不合法", "code": 1} + return {"msg": cfg.lang["invalid_email"], "code": 1} if not (await db.check_user(username)): tkn = prep_uuid(uuid.uuid4().hex) emails[tkn] = (username, hashlib.sha256( @@ -169,45 +167,45 @@ async def login_callback(username: str = Form(), password: str = Form(), email: email_send_lst.append( (email, ROOT+"/api/checkemail?uid="+clean_uuid(tkn))) - return {"msg": "验证邮件已发送到邮箱,请在10分钟内完成验证", "code": 0} + return {"msg": cfg.lang["verification_email"].format(ACCESS_EMAIL_EXPIRE_MINUTES), "code": 0} else: - return {"msg": "用户名重复", "code": 1} + return {"msg": cfg.lang["username_exists"], "code": 1} @app.get("/api/checkemail") async def checkemail(uid: str): uid = prep_uuid(uid) if (uid not in emails): - return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "不存在的注册id"}) + return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": cfg.lang["invalid_regid"], "ui": cfg.config["ui"], "lang": cfg.lang}) if (emails[uid][2] < datetime.now()): del emails[uid] - return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "链接已过期"}) + return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": cfg.lang["invalid_regid"], "ui": cfg.config["ui"], "lang": cfg.lang}) if (emails[uid][1] == ""): - return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "不存在的注册id"}) + return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": cfg.lang["invalid_regid"], "ui": cfg.config["ui"], "lang": cfg.lang}) if await db.create_user(emails[uid][0], emails[uid][1], emails[uid][3]) == 0: del emails[uid] - return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "创建成功"}) + return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": cfg.lang["created_successfully"], "ui": cfg.config["ui"], "lang": cfg.lang}) else: del emails[uid] - return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "重复的用户名"}) + return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": cfg.lang["username_exists"], "ui": cfg.config["ui"], "lang": cfg.lang}) @app.get("/api/resetpasswd", response_class=HTMLResponse) async def resetpasswd(uid: str, response: Response): uid = prep_uuid(uid) if (uid not in emails): - return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "不存在的验证id"}) + return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": cfg.lang["invalid_checkid"], "ui": cfg.config["ui"], "lang": cfg.lang}) if (emails[uid][2] < datetime.now()): del emails[uid] - return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "链接已过期"}) + return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": cfg.lang["invalid_checkid"], "ui": cfg.config["ui"], "lang": cfg.lang}) if (emails[uid][1] != ""): - return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "不存在的注册id"}) + return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": cfg.lang["invalid_checkid"], "ui": cfg.config["ui"], "lang": cfg.lang}) tokennow = await create_token(emails[uid][0]) tkn = prep_uuid(uuid.uuid4().hex) apikeys[tkn] = tokens[tokennow] response.set_cookie("session", clean_uuid(tokennow)) del emails[uid] - return '正在跳转正在跳转' + return f'{cfg.lang["redirect"]}{cfg.lang["redirect"]}' @app.post("/api/send_resetpasswd") @@ -220,9 +218,9 @@ async def resetpasswd(username: str = Form()): email_send_lst.append( (email, ROOT+"/api/resetpasswd?uid="+clean_uuid(tkn))) - return {"msg": "验证邮件已发送到邮箱,请在10分钟内完成验证", "code": 0} + return {"msg": cfg.lang["verification_email"].format(ACCESS_EMAIL_EXPIRE_MINUTES), "code": 0} else: - return {"msg": "用户名不存在", "code": 1} + return {"msg": cfg.lang["username_not_exists"], "code": 1} @app.get("/api/getinfo") @@ -230,14 +228,14 @@ async def get_user_info(uid: str): uid = prep_uuid(uid) username = await check_apikey(uid) if (username == ""): - return {"code": 1, "msg": "token无效", "data": {}} + return {"code": 1, "msg": cfg.lang["invalid_token"], "data": {}} return {"code": 0, "msg": "", "data": {"username": username, "email": await db.get_email(username)}} @app.post("/api/changepasswd") async def changepasswd(password: str = Form(), session: Annotated[str | None, Cookie()] = None): if (check_passwd(password)): - return {"msg": "密码强度弱,请至少包含一个大小写字符与数字且长度>8"} + return {"msg": cfg.lang["weak_passwd"]} if (session is not None): session = prep_uuid(session) username = await check_token(session) @@ -247,9 +245,9 @@ async def changepasswd(password: str = Form(), session: Annotated[str | None, Co del tokens[session] return {"msg": ""} else: - return {"msg": "无效的登录token"} + return {"msg": cfg.lang["invalid_token"]} else: - return {"msg": "无效的登录token"} + return {"msg": cfg.lang["invalid_token"]} @app.get("/login") @@ -257,7 +255,7 @@ async def login(state: str = "", client_id: str = "", redirect_url: str = "/user now_redirect_url = redirect_url.replace( "https://", "").replace("http://", "").split("#")[0].rstrip("/") if (now_redirect_url not in REDIRECT_URL_WHITELIST): - return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": "无效的重定向URL"}) + return templates.TemplateResponse("checkemail.html", {"request": {}, "msg": cfg.lang["invalid_redirect_url"], "ui": cfg.config["ui"], "lang": cfg.lang}) if (session is not None): session = prep_uuid(session) username = await check_token(session) @@ -265,12 +263,12 @@ async def login(state: str = "", client_id: str = "", redirect_url: str = "/user tkn = prep_uuid(uuid.uuid4().hex) apikeys[tkn] = tokens[session] return RedirectResponse(url=redirect_url+f"#access_token={clean_uuid(tkn)}&token_type=Bearer&state={state}") - return templates.TemplateResponse("login.html", {"request": {}, "redirect_url": redirect_url, "state": state}) + return templates.TemplateResponse("login.html", {"request": {}, "redirect_url": redirect_url, "state": state, "ui": cfg.config["ui"], "lang": cfg.lang}) @app.get("/signup") async def login(redirect_url: str): - return templates.TemplateResponse("signup.html", {"request": {}, "redirect_url": redirect_url}) + return templates.TemplateResponse("signup.html", {"request": {}, "redirect_url": redirect_url, "ui": cfg.config["ui"], "lang": cfg.lang}) @app.get("/user") @@ -281,12 +279,12 @@ async def login(session: Annotated[str | None, Cookie()] = None): username = await check_token(session) if (username == ""): return RedirectResponse(url="/login?redirect_url=/user") - return templates.TemplateResponse("manage.html", {"request": {}}) + return templates.TemplateResponse("manage.html", {"request": {}, "ui": cfg.config["ui"], "lang": cfg.lang}) @app.get("/resetpasswd") async def resetpasswd(response: Response): - return templates.TemplateResponse("resetpasswd.html", {"request": {}}) + return templates.TemplateResponse("resetpasswd.html", {"request": {}, "ui": cfg.config["ui"], "lang": cfg.lang}) @app.get("/manager/init") diff --git a/src/checkemail.html b/src/checkemail.html index f68a80f..8f759b2 100644 --- a/src/checkemail.html +++ b/src/checkemail.html @@ -4,7 +4,7 @@ - StuyAreaCN Accout System + {{ui.title}}