Compare commits

..

No commits in common. "51e22a4611dab8edcd7295e4208233e21a49f8ca" and "5c67fb517921060fe3cc0afa887ecd6521ad27ae" have entirely different histories.

5 changed files with 340 additions and 423 deletions

View File

@ -3,83 +3,68 @@ import inProd from "./inProd";
import env from "./env-vars"; import env from "./env-vars";
import * as T from "../../../website/src/types/account-types"; import * as T from "../../../website/src/types/account-types";
type Address = string | { name: string; address: string }; type Address = string | { name: string, address: string };
const adminAddress: Address = { const adminAddress: Address = {
name: "LingDocs Admin", name: "LingDocs Admin",
address: "admin@lingdocs.com", address: "admin@lingdocs.com",
}; };
export function getAddress(user: T.LingdocsUser): Address { function getAddress(user: T.LingdocsUser): Address {
// TODO: Guard against "" // TODO: Guard against ""
if (!user.name) return user.email || ""; if (!user.name) return user.email || "";
return { return {
name: user.name, name: user.name,
address: user.email || "", address: user.email || "",
}; };
} }
const transporter = nodemailer.createTransport({ const transporter = nodemailer.createTransport({
host: env.emailHost, host: env.emailHost,
port: 465, port: 465,
secure: true, secure: true,
auth: { auth: {
user: env.emailUser, user: env.emailUser,
pass: env.emailPass, pass: env.emailPass,
}, },
}); });
async function sendEmail(to: Address, subject: string, text: string) { async function sendEmail(to: Address, subject: string, text: string) {
return await transporter.sendMail({ await transporter.sendMail({
from: adminAddress, from: adminAddress,
to, to,
subject, subject,
text, text,
}); });
} }
// TODO: MAKE THIS A URL ACROSS PROJECT // TODO: MAKE THIS A URL ACROSS PROJECT
const baseURL = inProd const baseURL = inProd ? "https://account.lingdocs.com" : "http://localhost:4000";
? "https://account.lingdocs.com"
: "http://localhost:4000";
export async function sendVerificationEmail({ export async function sendVerificationEmail(user: T.LingdocsUser, token: T.URLToken) {
name, const subject = "Please Verify Your E-mail";
uid, const content = `Hello ${user.name},
email,
token,
}: {
name: string;
uid: T.UUID;
email: string;
token: T.URLToken;
}) {
const subject = "Please Verify Your E-mail";
const content = `Hello ${name},
Please verify your email by visiting this link: ${baseURL}/email-verification/${uid}/${token} Please verify your email by visiting this link: ${baseURL}/email-verification/${user.userId}/${token}
LingDocs Admin`; LingDocs Admin`;
await sendEmail(email, subject, content); await sendEmail(getAddress(user), subject, content);
} }
export async function sendPasswordResetEmail( export async function sendPasswordResetEmail(user: T.LingdocsUser, token: T.URLToken) {
user: T.LingdocsUser, const subject = "Reset Your Password";
token: T.URLToken const content = `Hello ${user.name},
) {
const subject = "Reset Your Password";
const content = `Hello ${user.name},
Please visit this link to reset your password: ${baseURL}/password-reset/${user.userId}/${token} Please visit this link to reset your password: ${baseURL}/password-reset/${user.userId}/${token}
LingDocs Admin`; LingDocs Admin`;
await sendEmail(getAddress(user), subject, content); await sendEmail(getAddress(user), subject, content);
} }
export async function sendAccountUpgradeMessage(user: T.LingdocsUser) { export async function sendAccountUpgradeMessage(user: T.LingdocsUser) {
const subject = "You're Upgraded to Student"; const subject = "You're Upgraded to Student";
const content = `Hello ${user.name}, const content = `Hello ${user.name},
Congratulations on your upgrade to a LingDocs Student account! 👨🎓 Congratulations on your upgrade to a LingDocs Student account! 👨🎓
@ -87,13 +72,11 @@ Now you can start using your wordlist in the dictionary. It will automatically s
LingDocs Admin`; LingDocs Admin`;
await sendEmail(getAddress(user), subject, content); await sendEmail(getAddress(user), subject, content);
} }
export async function sendUpgradeRequestToAdmin( export async function sendUpgradeRequestToAdmin(userWantingToUpgrade: T.LingdocsUser) {
userWantingToUpgrade: T.LingdocsUser const subject = "Account Upgrade Request";
) { const content = `${userWantingToUpgrade.name} - ${userWantingToUpgrade.email} - ${userWantingToUpgrade.userId} is requesting to upgrade to student.`;
const subject = "Account Upgrade Request"; await sendEmail(adminAddress, subject, content);
const content = `${userWantingToUpgrade.name} - ${userWantingToUpgrade.email} - ${userWantingToUpgrade.userId} is requesting to upgrade to student.`; }
await sendEmail(adminAddress, subject, content);
}

View File

@ -1,15 +1,18 @@
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { import {
insertLingdocsUser, insertLingdocsUser,
addCouchDbAuthUser, addCouchDbAuthUser,
updateLingdocsUser, updateLingdocsUser,
deleteCouchDbAuthUser, deleteCouchDbAuthUser,
} from "../lib/couch-db"; } from "../lib/couch-db";
import { getHash, getEmailTokenAndHash } from "../lib/password-utils"; import {
getHash,
getEmailTokenAndHash,
} from "../lib/password-utils";
import { getTimestamp } from "../lib/time-utils"; import { getTimestamp } from "../lib/time-utils";
import { import {
sendVerificationEmail, sendVerificationEmail,
sendAccountUpgradeMessage, sendAccountUpgradeMessage,
} from "../lib/mail-utils"; } from "../lib/mail-utils";
import { outsideProviders } from "../middleware/setup-passport"; import { outsideProviders } from "../middleware/setup-passport";
import * as T from "../../../website/src/types/account-types"; import * as T from "../../../website/src/types/account-types";
@ -17,212 +20,183 @@ import env from "../lib/env-vars";
import Stripe from "stripe"; import Stripe from "stripe";
const stripe = new Stripe(env.stripeSecretKey, { const stripe = new Stripe(env.stripeSecretKey, {
apiVersion: "2022-08-01", apiVersion: "2022-08-01",
}); });
function getUUID(): T.UUID { function getUUID(): T.UUID {
return uuidv4() as T.UUID; return uuidv4() as T.UUID;
} }
export function canRemoveOneOutsideProvider(user: T.LingdocsUser): boolean { export function canRemoveOneOutsideProvider(user: T.LingdocsUser): boolean {
if (user.email && user.password) { if (user.email && user.password) {
return true; return true;
} }
const providersPresent = outsideProviders.filter( const providersPresent = outsideProviders.filter((provider) => !!user[provider]);
(provider) => !!user[provider] return providersPresent.length > 1;
);
return providersPresent.length > 1;
} }
export function getVerifiedEmail({ export function getVerifiedEmail({ emails }: T.ProviderProfile): string | false {
emails, return (
}: T.ProviderProfile): string | false { emails
return emails && && emails.length
emails.length && // @ts-ignore
// @ts-ignore && emails[0].verified
emails[0].verified ) ? emails[0].value : false;
? emails[0].value
: false;
} }
export function getEmailFromGoogleProfile(profile: T.GoogleProfile): { export function getEmailFromGoogleProfile(profile: T.GoogleProfile): { email: string | undefined, verified: boolean } {
email: string | undefined; if (!profile.emails || profile.emails.length === 0) {
verified: boolean; return { email: undefined, verified: false };
} { }
if (!profile.emails || profile.emails.length === 0) { const em = profile.emails[0];
return { email: undefined, verified: false }; // @ts-ignore // but the verified value *is* there - if not it's still safe
} const verified = !!em.verified
const em = profile.emails[0]; return {
// @ts-ignore // but the verified value *is* there - if not it's still safe email: em.value,
const verified = !!em.verified; verified,
return { };
email: em.value,
verified,
};
} }
export async function upgradeUser( export async function upgradeUser(userId: T.UUID, subscription?: T.StripeSubscription): Promise<T.UpgradeUserResponse> {
userId: T.UUID, // add user to couchdb authentication db
subscription?: T.StripeSubscription const { password, userDbName } = await addCouchDbAuthUser(userId);
): Promise<T.UpgradeUserResponse> { // // create user db
// add user to couchdb authentication db // update LingdocsUser
const { password, userDbName } = await addCouchDbAuthUser(userId); const user = await updateLingdocsUser(userId, {
// // create user db level: "student",
// update LingdocsUser wordlistDbName: userDbName,
const user = await updateLingdocsUser(userId, { couchDbPassword: password,
level: "student", upgradeToStudentRequest: undefined,
wordlistDbName: userDbName, subscription,
couchDbPassword: password, });
upgradeToStudentRequest: undefined, if (user.email) {
subscription, sendAccountUpgradeMessage(user).catch(console.error);
}); }
if (user.email) { return {
sendAccountUpgradeMessage(user).catch(console.error); ok: true,
} message: "user upgraded to student",
return { user,
ok: true, };
message: "user upgraded to student",
user,
};
} }
export async function downgradeUser( export async function downgradeUser(userId: T.UUID, subscriptionId?: string): Promise<T.DowngradeUserResponse> {
userId: T.UUID, await deleteCouchDbAuthUser(userId);
subscriptionId?: string if (subscriptionId) {
): Promise<T.DowngradeUserResponse> { stripe.subscriptions.del(subscriptionId);
await deleteCouchDbAuthUser(userId); }
if (subscriptionId) { const user = await updateLingdocsUser(userId, {
stripe.subscriptions.del(subscriptionId); level: "basic",
} wordlistDbName: undefined,
const user = await updateLingdocsUser(userId, { couchDbPassword: undefined,
level: "basic", upgradeToStudentRequest: undefined,
wordlistDbName: undefined, subscription: undefined,
couchDbPassword: undefined, });
upgradeToStudentRequest: undefined, if (user.email) {
subscription: undefined, // TODO
}); // sendAccountDowngradeMessage(user).catch(console.error);
if (user.email) { }
// TODO return {
// sendAccountDowngradeMessage(user).catch(console.error); ok: true,
} message: "user downgraded to basic",
return { user,
ok: true, };
message: "user downgraded to basic",
user,
};
} }
export async function denyUserUpgradeRequest(userId: T.UUID): Promise<void> { export async function denyUserUpgradeRequest(userId: T.UUID): Promise<void> {
await updateLingdocsUser(userId, { await updateLingdocsUser(userId, {
upgradeToStudentRequest: "denied", upgradeToStudentRequest: "denied",
}); });
} }
export async function createNewUser( export async function createNewUser(input: {
input: strategy: "local",
| { email: string,
strategy: "local"; name: string,
email: string; passwordPlainText: string,
name: string; } | {
passwordPlainText: string; strategy: "github",
} profile: T.GitHubProfile,
| { } | {
strategy: "github"; strategy: "google",
profile: T.GitHubProfile; profile: T.GoogleProfile,
} } | {
| { strategy: "twitter",
strategy: "google"; profile: T.TwitterProfile,
profile: T.GoogleProfile; }): Promise<T.LingdocsUser> {
} const userId = getUUID();
| { const now = getTimestamp();
strategy: "twitter"; if (input.strategy === "local") {
profile: T.TwitterProfile; const email = await getEmailTokenAndHash();
} const password = await getHash(input.passwordPlainText);
): Promise<T.LingdocsUser> { const newUser: T.LingdocsUser = {
const userId = getUUID(); _id: userId,
const now = getTimestamp(); userId,
if (input.strategy === "local") { email: input.email,
const email = await getEmailTokenAndHash(); emailVerified: email.hash,
const password = await getHash(input.passwordPlainText); name: input.name,
password,
level: "basic",
tests: [],
accountCreated: now,
lastLogin: now,
lastActive: now,
};
const user = await insertLingdocsUser(newUser);
sendVerificationEmail(user, email.token).catch(console.error);
return user;
}
// GitHub || Twitter
if (input.strategy === "github" || input.strategy === "twitter") {
const newUser: T.LingdocsUser = {
_id: userId,
userId,
emailVerified: false,
name: input.profile.displayName,
[input.strategy]: input.profile,
level: "basic",
tests: [],
accountCreated: now,
lastLogin: now,
lastActive: now,
};
const user = await insertLingdocsUser(newUser);
return user;
}
// Google
// TODO: Add e-mail in here
const { email, verified } = getEmailFromGoogleProfile(input.profile);
if (email && !verified) {
const em = await getEmailTokenAndHash();
const newUser: T.LingdocsUser = {
_id: userId,
userId,
email,
emailVerified: em.hash,
name: input.profile.displayName,
google: input.profile,
lastLogin: now,
tests: [],
lastActive: now,
accountCreated: now,
level: "basic",
}
const user = await insertLingdocsUser(newUser);
sendVerificationEmail(user, em.token);
return user;
}
const newUser: T.LingdocsUser = { const newUser: T.LingdocsUser = {
_id: userId, _id: userId,
userId, userId,
email: input.email, email,
emailVerified: email.hash, emailVerified: verified,
name: input.name, name: input.profile.displayName,
password, google: input.profile,
level: "basic", lastLogin: now,
tests: [], tests: [],
accountCreated: now, lastActive: now,
lastLogin: now, accountCreated: now,
lastActive: now, level: "basic",
}; }
await sendVerificationEmail({
name: input.name,
uid: userId,
email: input.email || "",
token: email.token,
});
const user = await insertLingdocsUser(newUser); const user = await insertLingdocsUser(newUser);
return user; return user;
}
// GitHub || Twitter
if (input.strategy === "github" || input.strategy === "twitter") {
const newUser: T.LingdocsUser = {
_id: userId,
userId,
emailVerified: false,
name: input.profile.displayName,
[input.strategy]: input.profile,
level: "basic",
tests: [],
accountCreated: now,
lastLogin: now,
lastActive: now,
};
const user = await insertLingdocsUser(newUser);
return user;
}
// Google
// TODO: Add e-mail in here
const { email, verified } = getEmailFromGoogleProfile(input.profile);
if (email && !verified) {
const em = await getEmailTokenAndHash();
const newUser: T.LingdocsUser = {
_id: userId,
userId,
email,
emailVerified: em.hash,
name: input.profile.displayName,
google: input.profile,
lastLogin: now,
tests: [],
lastActive: now,
accountCreated: now,
level: "basic",
};
const user = await insertLingdocsUser(newUser);
sendVerificationEmail({
name: newUser.name,
uid: newUser.userId,
email: newUser.email || "",
token: em.token,
}).catch(console.error);
return user;
}
const newUser: T.LingdocsUser = {
_id: userId,
userId,
email,
emailVerified: verified,
name: input.profile.displayName,
google: input.profile,
lastLogin: now,
tests: [],
lastActive: now,
accountCreated: now,
level: "basic",
};
const user = await insertLingdocsUser(newUser);
return user;
} }

View File

@ -1,210 +1,184 @@
import express, { Response } from "express"; import express, { Response } from "express";
import { import {
deleteLingdocsUser, deleteLingdocsUser,
getLingdocsUser, getLingdocsUser,
updateLingdocsUser, updateLingdocsUser,
} from "../lib/couch-db"; } from "../lib/couch-db";
import { import {
getHash, getHash,
compareToHash, compareToHash,
getEmailTokenAndHash, getEmailTokenAndHash,
} from "../lib/password-utils"; } from "../lib/password-utils";
import { import {
sendUpgradeRequestToAdmin, sendUpgradeRequestToAdmin,
sendVerificationEmail, sendVerificationEmail,
} from "../lib/mail-utils"; } from "../lib/mail-utils";
import { upgradeUser } from "../lib/user-utils"; import {
upgradeUser,
} from "../lib/user-utils";
import * as T from "../../../website/src/types/account-types"; import * as T from "../../../website/src/types/account-types";
import env from "../lib/env-vars"; import env from "../lib/env-vars";
// TODO: ADD PROPER ERROR HANDLING THAT WILL RETURN JSON ALWAYS // TODO: ADD PROPER ERROR HANDLING THAT WILL RETURN JSON ALWAYS
function sendResponse(res: Response, payload: T.APIResponse) { function sendResponse(res: Response, payload: T.APIResponse) {
return res.send(payload); return res.send(payload);
} }
const apiRouter = express.Router(); const apiRouter = express.Router();
// Guard all api with authentication // Guard all api with authentication
apiRouter.use((req, res, next) => { apiRouter.use((req, res, next) => {
if (req.isAuthenticated()) { if (req.isAuthenticated()) {
return next(); return next();
} }
const r: T.APIResponse = { ok: false, error: "401 Unauthorized" }; const r: T.APIResponse = { ok: false, error: "401 Unauthorized" };
return res.status(401).send(r); return res.status(401).send(r);
}); });
/** /**
* gets the LingdocsUser object for the user signed in * gets the LingdocsUser object for the user signed in
*/ */
apiRouter.get("/user", (req, res, next) => { apiRouter.get("/user", (req, res, next) => {
if (!req.user) return next("user not found"); if (!req.user) return next("user not found");
sendResponse(res, { ok: true, user: req.user }); sendResponse(res, { ok: true, user: req.user });
}); });
/** /**
* adds (passed) test results to the record of the user signed in * adds (passed) test results to the record of the user signed in
*/ */
apiRouter.put("/user/tests", async (req, res, next) => { apiRouter.put("/user/tests", async (req, res, next) => {
if (!req.user) return next("user not found"); if (!req.user) return next("user not found");
try { try {
const { tests } = req.body as T.PostTestResultsBody; const { tests } = req.body as T.PostTestResultsBody;
await updateLingdocsUser(req.user.userId, { tests }); await updateLingdocsUser(req.user.userId, { tests });
sendResponse(res, { sendResponse(res, {
ok: true, ok: true,
message: "posted test results", message: "posted test results",
tests, tests,
}); });
} catch (e) { } catch(e) {
next(e); next(e);
} }
}); });
/** /**
* receives a request to change or add a user's own password * receives a request to change or add a user's own password
*/ */
apiRouter.post("/password", async (req, res, next) => { apiRouter.post("/password", async (req, res, next) => {
if (!req.user) return next("user not found"); if (!req.user) return next("user not found");
const { oldPassword, password, passwordConfirmed } = req.body; const { oldPassword, password, passwordConfirmed } = req.body;
const addingFirstPassword = !req.user.password; const addingFirstPassword = !req.user.password;
if (!oldPassword && !addingFirstPassword) { if (!oldPassword && !addingFirstPassword) {
return sendResponse(res, { return sendResponse(res, { ok: false, error: "Please enter your old password" });
ok: false,
error: "Please enter your old password",
});
}
if (!password) {
return sendResponse(res, {
ok: false,
error: "Please enter a new password",
});
}
if (!req.user.email) {
return sendResponse(res, {
ok: false,
error: "You need to add an e-mail address first",
});
}
if (req.user.password) {
const matchedOld =
(await compareToHash(oldPassword, req.user.password)) ||
!req.user.password;
if (!matchedOld) {
return sendResponse(res, { ok: false, error: "Incorrect old password" });
} }
} if (!password) {
if (password !== passwordConfirmed) { return sendResponse(res, { ok: false, error: "Please enter a new password" });
return sendResponse(res, { }
ok: false, if (!req.user.email) {
error: "New passwords do not match", return sendResponse(res, { ok: false, error: "You need to add an e-mail address first" });
}); }
} if (req.user.password) {
if (password.length < 6) { const matchedOld = await compareToHash(oldPassword, req.user.password) || !req.user.password;
return sendResponse(res, { ok: false, error: "New password too short" }); if (!matchedOld) {
} return sendResponse(res, { ok: false, error: "Incorrect old password" });
const hash = await getHash(password); }
await updateLingdocsUser(req.user.userId, { password: hash }); }
sendResponse(res, { if (password !== passwordConfirmed) {
ok: true, return sendResponse(res, { ok: false, error: "New passwords do not match" });
message: addingFirstPassword ? "Password added" : "Password changed", }
}); if (password.length < 6) {
return sendResponse(res, {ok: false, error: "New password too short" });
}
const hash = await getHash(password);
await updateLingdocsUser(req.user.userId, { password: hash });
sendResponse(res, { ok: true, message: addingFirstPassword ? "Password added" : "Password changed" });
}); });
/** /**
* receives a request to generate a new e-mail verification token and send e-mail * receives a request to generate a new e-mail verification token and send e-mail
*/ */
apiRouter.put("/email-verification", async (req, res, next) => { apiRouter.put("/email-verification", async (req, res, next) => {
try { try {
if (!req.user) throw new Error("user not found"); if (!req.user) throw new Error("user not found");
const { token, hash } = await getEmailTokenAndHash(); const { token, hash } = await getEmailTokenAndHash();
const u = await updateLingdocsUser(req.user.userId, { const u = await updateLingdocsUser(req.user.userId, { emailVerified: hash });
emailVerified: hash, sendVerificationEmail(u, token).then(() => {
}); sendResponse(res, { ok: true, message: "e-mail verification sent" });
sendVerificationEmail({ }).catch((err) => {
name: u.name, sendResponse(res, { ok: false, error: err });
uid: u.userId, });
email: u.email || "", } catch (e) {
token, next(e);
}) }
.then(() => {
sendResponse(res, { ok: true, message: "e-mail verification sent" });
})
.catch((err) => {
sendResponse(res, { ok: false, error: err });
});
} catch (e) {
next(e);
}
}); });
apiRouter.put("/user/upgrade", async (req, res, next) => { apiRouter.put("/user/upgrade", async (req, res, next) => {
if (!req.user) throw new Error("user not found"); if (!req.user) throw new Error("user not found");
try { try {
const givenPassword = (req.body.password || "") as string; const givenPassword = (req.body.password || "") as string;
const studentPassword = env.upgradePassword; const studentPassword = env.upgradePassword;
if (givenPassword.toLowerCase().trim() !== studentPassword.toLowerCase()) { if (givenPassword.toLowerCase().trim() !== studentPassword.toLowerCase()) {
const wrongPass: T.UpgradeUserResponse = { const wrongPass: T.UpgradeUserResponse = {
ok: false, ok: false,
error: "incorrect password", error: "incorrect password",
}; };
res.send(wrongPass); res.send(wrongPass);
return; return;
}
const { userId } = req.user;
const user = await getLingdocsUser("userId", userId);
if (!user) throw new Error("user lost");
if (user.level !== "basic") {
const alreadyUpgraded: T.UpgradeUserResponse = {
ok: true,
message: "user already upgraded",
user,
};
res.send(alreadyUpgraded);
return;
}
const upgraded: T.UpgradeUserResponse = await upgradeUser(userId);
res.send(upgraded);
} catch (e) {
next(e);
} }
const { userId } = req.user;
const user = await getLingdocsUser("userId", userId);
if (!user) throw new Error("user lost");
if (user.level !== "basic") {
const alreadyUpgraded: T.UpgradeUserResponse = {
ok: true,
message: "user already upgraded",
user,
};
res.send(alreadyUpgraded);
return;
}
const upgraded: T.UpgradeUserResponse = await upgradeUser(userId);
res.send(upgraded);
} catch (e) {
next(e);
}
}); });
apiRouter.post("/user/upgradeToStudentRequest", async (req, res, next) => { apiRouter.post("/user/upgradeToStudentRequest", async (req, res, next) => {
if (!req.user) throw new Error("user not found"); if (!req.user) throw new Error("user not found");
try { try {
if (req.user.level === "student" || req.user.level === "editor") { if (req.user.level === "student" || req.user.level === "editor") {
res.send({ ok: true, message: "user already upgraded" }); res.send({ ok: true, message: "user already upgraded" });
return; return;
}
sendUpgradeRequestToAdmin(req.user).catch(console.error);
await updateLingdocsUser(req.user.userId, { upgradeToStudentRequest: "waiting" });
res.send({ ok: true, message: "request for upgrade sent" });
} catch (e) {
next(e);
} }
sendUpgradeRequestToAdmin(req.user).catch(console.error);
await updateLingdocsUser(req.user.userId, {
upgradeToStudentRequest: "waiting",
});
res.send({ ok: true, message: "request for upgrade sent" });
} catch (e) {
next(e);
}
}); });
/** /**
* deletes a users own account * deletes a users own account
*/ */
apiRouter.delete("/user", async (req, res, next) => { apiRouter.delete("/user", async (req, res, next) => {
try { try {
if (!req.user) throw new Error("user not found"); if (!req.user) throw new Error("user not found");
await deleteLingdocsUser(req.user.userId); await deleteLingdocsUser(req.user.userId);
sendResponse(res, { ok: true, message: "user deleted" }); sendResponse(res, { ok: true, message: "user deleted" });
} catch (e) { } catch (e) {
next(e); next(e);
} }
}); })
/** /**
* signs out the user signed in * signs out the user signed in
*/ */
apiRouter.post("/sign-out", (req, res) => { apiRouter.post("/sign-out", (req, res) => {
req.logOut(); req.logOut();
sendResponse(res, { ok: true, message: "signed out" }); sendResponse(res, { ok: true, message: "signed out" });
}); });
export default apiRouter; export default apiRouter;

View File

@ -22,7 +22,6 @@ import { upgradeUser, denyUserUpgradeRequest } from "../lib/user-utils";
import { validateReCaptcha } from "../lib/recaptcha"; import { validateReCaptcha } from "../lib/recaptcha";
import { getTimestamp } from "../lib/time-utils"; import { getTimestamp } from "../lib/time-utils";
import { import {
getAddress,
sendPasswordResetEmail, sendPasswordResetEmail,
sendVerificationEmail, sendVerificationEmail,
} from "../lib/mail-utils"; } from "../lib/mail-utils";
@ -75,13 +74,7 @@ const authRouter = (passport: PassportStatic) => {
email, email,
emailVerified: hash, emailVerified: hash,
}); });
// TODO: AWAIT THE E-MAIL SEND TO MAKE SURE THE E-MAIL WORKS! sendVerificationEmail(updated, token).catch(console.error);
sendVerificationEmail({
name: updated.name,
uid: updated.userId,
email: updated.email || "",
token,
});
return res.render(page, { return res.render(page, {
user: updated, user: updated,
error: null, error: null,
@ -176,23 +169,17 @@ const authRouter = (passport: PassportStatic) => {
try { try {
const { email, password, name } = req.body; const { email, password, name } = req.body;
const existingUser = await getLingdocsUser("email", email); const existingUser = await getLingdocsUser("email", email);
if (existingUser) if (existingUser) return res.send("User Already Exists");
return res.send({ ok: false, message: "User Already Exists" }); const user = await createNewUser({
try { strategy: "local",
const user = await createNewUser({ email,
strategy: "local", passwordPlainText: password,
email, name,
passwordPlainText: password, });
name, req.logIn(user, (err) => {
}); if (err) return next(err);
req.logIn(user, (err) => { return res.send({ ok: true, user });
if (err) return next(err); });
return res.send({ ok: true, user });
});
} catch (e) {
console.error(e);
return res.send({ ok: false, message: "Invalid E-mail" });
}
} catch (e) { } catch (e) {
next(e); next(e);
} }

View File

@ -29,7 +29,7 @@
<h1 class="h3 mb-4 fw-normal">Sign in to LingDocs</h1> <h1 class="h3 mb-4 fw-normal">Sign in to LingDocs</h1>
<!-- <p class="small mb-2">New? Enter an e-mail and password to sign up</p> --> <!-- <p class="small mb-2">New? Enter an e-mail and password to sign up</p> -->
<div class="form-floating mt-3"> <div class="form-floating mt-3">
<input type="email" required class="form-control" id="emailInput" placeholder="name@example.com"> <input type="email" pattern='(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])' required class="form-control" id="emailInput" placeholder="name@example.com">
<label for="floatingInput">Email address</label> <label for="floatingInput">Email address</label>
</div> </div>
<div class="form-floating"> <div class="form-floating">
@ -98,7 +98,6 @@
body: JSON.stringify({ email, password, name, token }), body: JSON.stringify({ email, password, name, token }),
}).then((res) => res.json()) }).then((res) => res.json())
.then((res) => { .then((res) => {
console.log({ res });
if (res.ok) { if (res.ok) {
location.reload(); location.reload();
} }