forgotten password routine
This commit is contained in:
20
cutt/mail.py
20
cutt/mail.py
@@ -1,10 +1,13 @@
|
||||
from email.message import EmailMessage
|
||||
from email.utils import formataddr
|
||||
from random import random
|
||||
import smtplib
|
||||
import os
|
||||
import ssl
|
||||
from time import sleep
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import Response, status
|
||||
from fastapi.responses import PlainTextResponse
|
||||
from pydantic import BaseModel
|
||||
from sqlmodel import Session, select
|
||||
from cutt.db import Player, TokenDB, engine
|
||||
from cutt.security import set_password_token
|
||||
@@ -23,10 +26,14 @@ def generate_password_link(user: Player):
|
||||
return f"https://cutt.0124816.xyz/setpassword?token={token}"
|
||||
|
||||
|
||||
def send_forgotten_password_link(email: str):
|
||||
class EmailRequest(BaseModel):
|
||||
email: str
|
||||
|
||||
|
||||
def send_forgotten_password_link(email: EmailRequest):
|
||||
with Session(engine) as session:
|
||||
user = session.exec(
|
||||
select(P).where(P.email == email, P.disabled != True)
|
||||
select(P).where(P.email == email.email, P.disabled != True)
|
||||
).one_or_none()
|
||||
if user and user.email:
|
||||
link = generate_password_link(user)
|
||||
@@ -51,8 +58,9 @@ def send_forgotten_password_link(email: str):
|
||||
server.starttls(context=context)
|
||||
server.login(os.environ["SMTP_USER"], os.environ["SMTP_PASS"])
|
||||
server.send_message(msg)
|
||||
else:
|
||||
sleep(random())
|
||||
|
||||
return Response(
|
||||
"a link will be sent to this email, if it belongs to an existing user.",
|
||||
status_code=status.HTTP_200_OK,
|
||||
return PlainTextResponse(
|
||||
"a link will be sent to this email, if it belongs to an existing user."
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ import { GraphComponent } from "./Network";
|
||||
import MVPChart from "./MVPChart";
|
||||
import { SetPassword } from "./SetPassword";
|
||||
import { Register } from "./Register";
|
||||
import { ForgotPassword } from "./Login";
|
||||
|
||||
const Maintenance = () => {
|
||||
return (
|
||||
@@ -28,6 +29,7 @@ function App() {
|
||||
<Routes>
|
||||
<Route path="/register" element={<Register />} />
|
||||
<Route path="/setpassword" element={<SetPassword />} />
|
||||
<Route path="/forgotten_password" element={<ForgotPassword />} />
|
||||
<Route
|
||||
path="/*"
|
||||
element={
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { currentUser, login, User } from "./api";
|
||||
import { FormEvent, useEffect, useState } from "react";
|
||||
import { baseUrl, currentUser, login, User } from "./api";
|
||||
import Header from "./Header";
|
||||
import { useLocation, useNavigate } from "react-router";
|
||||
import { Eye, EyeSlash } from "./Icons";
|
||||
@@ -35,7 +35,7 @@ export const Login = ({ onLogin }: LoginProps) => {
|
||||
onLogin(user);
|
||||
}
|
||||
|
||||
function handleSubmit(e: React.FormEvent) {
|
||||
function handleSubmit(e: FormEvent) {
|
||||
e.preventDefault();
|
||||
doLogin();
|
||||
}
|
||||
@@ -86,6 +86,9 @@ export const Login = ({ onLogin }: LoginProps) => {
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<p className="help is-link has-text-right">
|
||||
<a href="forgotten_password">forgot password?</a>
|
||||
</p>
|
||||
{error && <p className="help is-danger">{error}</p>}
|
||||
</div>
|
||||
<div className="control">
|
||||
@@ -102,3 +105,81 @@ export const Login = ({ onLogin }: LoginProps) => {
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export const ForgotPassword = () => {
|
||||
const [email, setEmail] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
|
||||
async function handleSubmit(e: FormEvent) {
|
||||
e.preventDefault();
|
||||
const req = new Request(`${baseUrl}api/forgotten_password`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ email: decodeURI(email) }),
|
||||
});
|
||||
let resp: Response;
|
||||
try {
|
||||
resp = await fetch(req);
|
||||
} catch (e) {
|
||||
throw new Error(`request failed: ${e}`);
|
||||
}
|
||||
if (resp.ok) {
|
||||
const content = await resp.text();
|
||||
if (content) setError(content);
|
||||
}
|
||||
|
||||
if (!resp.ok) {
|
||||
const content = await resp.text();
|
||||
if (content) setError(content);
|
||||
else setError("unauthorized");
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="section is-medium">
|
||||
<div className="container is-max-tablet">
|
||||
<div className="block">
|
||||
<p className="level-item has-text-centered">
|
||||
<img
|
||||
className="image"
|
||||
alt="cool ultimate team tool"
|
||||
src="cutt.svg"
|
||||
style={{ width: 200 }}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<h1 className="title">Forgot password</h1>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="field">
|
||||
<div className="field">
|
||||
<label className="label">email</label>
|
||||
<div className="control">
|
||||
<input
|
||||
className="input"
|
||||
required
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
setError("");
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="field">
|
||||
<p className={"help" + (error ? " is-danger" : " is-success")}>
|
||||
{error}
|
||||
</p>
|
||||
</div>
|
||||
<div className="field is-grouped is-grouped-centered">
|
||||
<button className="button is-light is-primary">send email</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -21,4 +21,5 @@ dependencies = [
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pyqt6>=6.10.1",
|
||||
"python-dotenv>=1.2.1",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user