upgrade request function
This commit is contained in:
parent
b4d6b14825
commit
3f798d5534
|
@ -203,7 +203,7 @@ server {
|
|||
When a user upgrades their account level to `student` or `editor`:
|
||||
|
||||
1. A doc in the `_users` db is created with their Firebase Authentication info, account level, and a password they can use for syncing their personal wordlistdb
|
||||
2. A user database is created (by the firebase functions - *not* by the couchdb_peruser) which they use to sync their personal wordlist.
|
||||
2. A user database is created (automatically by `couchdb_peruser`) which they use to sync their personal wordlist.
|
||||
|
||||
There is also a `review-tasks` database which is used to store all the review tasks for editors and syncs with the review tasks in the app for the editor(s).
|
||||
|
||||
|
|
|
@ -95,8 +95,10 @@ export async function updateLingdocsUser(uuid: T.UUID, toUpdate:
|
|||
level: "student",
|
||||
wordlistDbName: T.WordlistDbName,
|
||||
couchDbPassword: T.UserDbPassword,
|
||||
requestedUpgradeToStudent: undefined,
|
||||
} |
|
||||
{ userTextOptionsRecord: T.UserTextOptionsRecord }
|
||||
{ userTextOptionsRecord: T.UserTextOptionsRecord } |
|
||||
{ requestedUpgradeToStudent: true }
|
||||
): Promise<T.LingdocsUser> {
|
||||
const user = await getLingdocsUser("userId", uuid);
|
||||
if (!user) throw new Error("unable to update - user not found " + uuid);
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as T from "../../../website/src/lib/account-types";
|
|||
|
||||
type Address = string | { name: string, address: string };
|
||||
|
||||
const from: Address = {
|
||||
const adminAddress: Address = {
|
||||
name: "LingDocs Admin",
|
||||
address: "admin@lingdocs.com",
|
||||
};
|
||||
|
@ -31,31 +31,53 @@ const transporter = nodemailer.createTransport({
|
|||
|
||||
async function sendEmail(to: Address, subject: string, text: string) {
|
||||
await transporter.sendMail({
|
||||
from,
|
||||
from: adminAddress,
|
||||
to,
|
||||
subject,
|
||||
text,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: MAKE THIS
|
||||
// TODO: MAKE THIS A URL ACROSS PROJECT
|
||||
const baseURL = inProd ? "https://account.lingdocs.com" : "http://localhost:4000";
|
||||
|
||||
export async function sendVerificationEmail(user: T.LingdocsUser, token: T.URLToken) {
|
||||
const subject = "Please Verify Your E-mail";
|
||||
const content = `Hello ${user.name},
|
||||
|
||||
Please verify your email by visiting this link: ${baseURL}/email-verification/${user.userId}/${token}
|
||||
|
||||
LingDocs Admin`;
|
||||
await sendEmail(getAddress(user), "Please Verify Your E-mail", content);
|
||||
await sendEmail(getAddress(user), subject, content);
|
||||
}
|
||||
|
||||
export async function sendPasswordResetEmail(user: T.LingdocsUser, token: T.URLToken) {
|
||||
const subject = "Reset Your Password";
|
||||
const content = `Hello ${user.name},
|
||||
|
||||
Please visit this link to reset your password: ${baseURL}/password-reset/${user.userId}/${token}
|
||||
|
||||
LingDocs Admin`;
|
||||
|
||||
await sendEmail(getAddress(user), "Reset Your Password", content);
|
||||
await sendEmail(getAddress(user), subject, content);
|
||||
}
|
||||
|
||||
export async function sendAccountUpgradeMessage(user: T.LingdocsUser) {
|
||||
const subject = "You've Upgraded to Student";
|
||||
const content = `Hello ${user.name},
|
||||
|
||||
Congratulations on your upgrade to a LingDocs Student account! 👨🎓
|
||||
|
||||
Now you can start using your wordlist in the dictionary. It will automatically sync across any devices you're signed in to.
|
||||
|
||||
LingDocs Admin`;
|
||||
|
||||
await sendEmail(getAddress(user), subject, content);
|
||||
}
|
||||
|
||||
export async function sendUpgradeRequestToAdmin(user: T.LingdocsUser) {
|
||||
const subject = "Account Upgrade Request";
|
||||
const content = `${user.name} - ${user.email} - ${user.userId} is requesting to upgrade to student.`;
|
||||
|
||||
await sendEmail(adminAddress, subject, content);
|
||||
}
|
|
@ -12,6 +12,8 @@ import {
|
|||
getEmailTokenAndHash,
|
||||
} from "../lib/password-utils";
|
||||
import {
|
||||
sendAccountUpgradeMessage,
|
||||
sendUpgradeRequestToAdmin,
|
||||
sendVerificationEmail,
|
||||
} from "../lib/mail-utils";
|
||||
import * as T from "../../../website/src/lib/account-types";
|
||||
|
@ -103,7 +105,7 @@ apiRouter.put("/user/userTextOptionsRecord", async (req, res, next) => {
|
|||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
apiRouter.put("/user/upgrade", async (req, res, next) => {
|
||||
if (!req.user) throw new Error("user not found");
|
||||
|
@ -133,9 +135,14 @@ apiRouter.put("/user/upgrade", async (req, res, next) => {
|
|||
// add user to couchdb authentication db
|
||||
const { password, userDbName } = await addCouchDbAuthUser(userId);
|
||||
// // create user db
|
||||
// const { name } = await createWordlistDatabase(userId, password);
|
||||
// update LingdocsUser
|
||||
const u = await updateLingdocsUser(userId, { level: "student", wordlistDbName: userDbName, couchDbPassword: password });
|
||||
const u = await updateLingdocsUser(userId, {
|
||||
level: "student",
|
||||
wordlistDbName: userDbName,
|
||||
couchDbPassword: password,
|
||||
requestedUpgradeToStudent: undefined,
|
||||
});
|
||||
sendAccountUpgradeMessage(u).catch(console.error);
|
||||
const upgraded: T.UpgradeUserResponse = {
|
||||
ok: true,
|
||||
message: "user upgraded to student",
|
||||
|
@ -147,6 +154,20 @@ apiRouter.put("/user/upgrade", async (req, res, next) => {
|
|||
}
|
||||
});
|
||||
|
||||
apiRouter.post("/user/upgradeToStudentRequest", async (req, res, next) => {
|
||||
if (!req.user) throw new Error("user not found");
|
||||
try {
|
||||
if (req.user.level === "student" || req.user.level === "editor") {
|
||||
res.send({ ok: true, message: "user already upgraded "});
|
||||
return;
|
||||
}
|
||||
sendUpgradeRequestToAdmin(req.user).catch(console.error);
|
||||
await updateLingdocsUser(req.user.userId, { requestedUpgradeToStudent: true });
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* deletes a users own account
|
||||
*/
|
||||
|
|
|
@ -38,6 +38,7 @@ export type LingdocsUser = {
|
|||
tokenHash: Hash,
|
||||
requestedOn: TimeStamp,
|
||||
},
|
||||
requestedUpgradeToStudent?: boolean,
|
||||
tests: [],
|
||||
lastLogin: TimeStamp,
|
||||
lastActive: TimeStamp,
|
||||
|
|
|
@ -23,6 +23,10 @@ export async function upgradeAccount(password: string): Promise<AT.UpgradeUserRe
|
|||
return response as AT.UpgradeUserResponse;
|
||||
}
|
||||
|
||||
export async function upgradeToStudentRequest(): Promise<AT.APIResponse> {
|
||||
return await myFetch("account", "upgradeToStudentRequest", "POST") as AT.APIResponse;
|
||||
}
|
||||
|
||||
export async function updateUserTextOptionsRecord(userTextOptionsRecord: AT.UserTextOptionsRecord): Promise<AT.UpdateUserTextOptionsRecordResponse> {
|
||||
const response = await myFetch("account", "user/userTextOptionsRecord", "PUT", { userTextOptionsRecord }) as AT.UpdateUserTextOptionsRecordResponse;
|
||||
return response;
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
upgradeAccount,
|
||||
signOut,
|
||||
publishDictionary,
|
||||
upgradeToStudentRequest,
|
||||
} from "../lib/backend-calls";
|
||||
import LoadingElipses from "../components/LoadingElipses";
|
||||
import { Helmet } from "react-helmet";
|
||||
|
@ -51,6 +52,22 @@ const Account = ({ user, loadUser }: { user: AT.LingdocsUser | undefined, loadUs
|
|||
setUpgradePassword("");
|
||||
setUpgradeError("");
|
||||
}
|
||||
async function handleUpgradeRequest() {
|
||||
setUpgradeError("");
|
||||
setWaiting(true);
|
||||
upgradeToStudentRequest().then((res) => {
|
||||
setWaiting(false);
|
||||
if (res.ok) {
|
||||
loadUser();
|
||||
closeUpgrade();
|
||||
} else {
|
||||
setUpgradeError("Error requesting upgrade");
|
||||
}
|
||||
}).catch((err) => {
|
||||
setWaiting(false);
|
||||
setUpgradeError(err.message);
|
||||
});
|
||||
}
|
||||
async function handleUpgrade() {
|
||||
setUpgradeError("");
|
||||
setWaiting(true);
|
||||
|
@ -136,7 +153,7 @@ const Account = ({ user, loadUser }: { user: AT.LingdocsUser | undefined, loadUs
|
|||
</div>
|
||||
</div>
|
||||
</li>}
|
||||
<li className="list-group-item">Account Level: {capitalize(user.level)}</li>
|
||||
<li className="list-group-item">Account Level: {capitalize(user.level)} {user.requestedUpgradeToStudent ? "(Upgrade Requested)" : ""}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -167,7 +184,7 @@ const Account = ({ user, loadUser }: { user: AT.LingdocsUser | undefined, loadUs
|
|||
<Modal.Header closeButton>
|
||||
<Modal.Title>Upgrade Account</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>Enter the secret upgrade password to upgrade your account.</Modal.Body>
|
||||
<Modal.Body>Enter the secret upgrade password to upgrade your account or <a href="javascript:;" onClick={handleUpgradeRequest}>request an upgrade</a>.</Modal.Body>
|
||||
<div className="form-group px-3">
|
||||
<label htmlFor="upgradePasswordForm">Upgrade password:</label>
|
||||
<input
|
||||
|
|
Loading…
Reference in New Issue