"""
Overleaf 自助注册服务
提供简单的注册页面,用户输入邮箱和密码即可注册
内部通过管理员账号调用 Overleaf API 创建用户
"""
from fastapi import FastAPI, Request, Form
from fastapi.responses import HTMLResponse
import requests
import os
app = FastAPI()
OL_URL = os.getenv("OL_INSTANCE", "http://overleaf")
OL_ADMIN_EMAIL = os.getenv("OL_ADMIN_EMAIL", "")
OL_ADMIN_PASSWORD = os.getenv("OL_ADMIN_PASSWORD", "")
REGISTER_HTML = """
注册 - LaTeX 论文编辑器
注册账号
LaTeX 论文编辑器
已有账号?去登录
MSG_PLACEHOLDER
""".replace("OLURL", OL_URL)
class OverleafAdmin:
def __init__(self):
self.session = requests.Session()
self.logged_in = False
def _get_csrf(self, path="/login"):
resp = self.session.get(f"{OL_URL}{path}")
# 从页面中提取 CSRF token
import re
match = re.search(r'name="_csrf"[^>]*value="([^"]+)"', resp.text)
if not match:
match = re.search(r'ol-csrfToken["\s]*content="([^"]+)"', resp.text)
if not match:
match = re.search(r'csrfToken["\s]*:\s*"([^"]+)"', resp.text)
return match.group(1) if match else ""
def login(self):
csrf = self._get_csrf("/login")
resp = self.session.post(f"{OL_URL}/login", data={
"_csrf": csrf,
"email": OL_ADMIN_EMAIL,
"password": OL_ADMIN_PASSWORD,
}, allow_redirects=False)
self.logged_in = resp.status_code in (200, 302)
return self.logged_in
def register_user(self, email):
if not self.logged_in:
self.login()
csrf = self._get_csrf("/admin/register")
resp = self.session.post(f"{OL_URL}/admin/register", data={
"_csrf": csrf,
"email": email,
})
# 从响应中提取设置密码的链接
import re
set_password_url = ""
match = re.search(r'(https?://[^\s"<>]*user/password/set\?[^\s"<>]+)', resp.text)
if match:
set_password_url = match.group(1)
return resp.status_code == 200, set_password_url
admin = OverleafAdmin()
@app.get("/", response_class=HTMLResponse)
async def index():
return REGISTER_HTML.replace("MSG_PLACEHOLDER", "")
@app.post("/register", response_class=HTMLResponse)
async def register(email: str = Form(...)):
try:
ok, set_url = admin.register_user(email)
if ok and set_url:
# 把链接中的 localhost 替换为实际访问地址
import re
set_url = re.sub(r'https?://[^/]+', OL_URL, set_url)
msg = f'注册成功!请点击下方链接设置密码:
设置密码
'
elif ok:
msg = f'注册成功!该邮箱可能已注册。请前往 登录页面 登录。
'
else:
msg = f'注册失败,请稍后重试。
'
except Exception as e:
msg = f'注册失败:{str(e)[:100]}
'
return REGISTER_HTML.replace("MSG_PLACEHOLDER", msg)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=18091)