make app usable without account, remove extra stuff

This commit is contained in:
m5r
2023-04-29 18:30:07 +02:00
parent cb35455722
commit 03ae466c66
128 changed files with 617 additions and 14061 deletions

View File

@ -1,130 +0,0 @@
import { type ActionFunction, json, redirect } from "@remix-run/node";
import { badRequest } from "remix-utils";
import { z } from "zod";
import SecurePassword from "secure-password";
import db from "~/utils/db.server";
import logger from "~/utils/logger.server";
import { hashPassword, requireLoggedIn, verifyPassword } from "~/utils/auth.server";
import { type FormError, validate } from "~/utils/validation.server";
import { destroySession, getSession } from "~/utils/session.server";
import deleteUserQueue from "~/queues/delete-user-data.server";
const action: ActionFunction = async ({ request }) => {
const formData = Object.fromEntries(await request.formData());
if (!formData._action) {
const errorMessage = "POST /settings/phone without any _action";
logger.error(errorMessage);
return badRequest({ errorMessage });
}
switch (formData._action as Action) {
case "deleteUser":
return deleteUser(request);
case "changePassword":
return changePassword(request, formData);
case "updateUser":
return updateUser(request, formData);
default:
const errorMessage = `POST /settings/phone with an invalid _action=${formData._action}`;
logger.error(errorMessage);
return badRequest({ errorMessage });
}
};
export default action;
async function deleteUser(request: Request) {
const {
user: { id },
} = await requireLoggedIn(request);
await db.user.update({
where: { id },
data: { hashedPassword: "pending deletion" },
});
await deleteUserQueue.add(`delete user ${id}`, { userId: id });
return redirect("/", {
headers: {
"Set-Cookie": await destroySession(await getSession(request)),
},
});
}
type ChangePasswordFailureActionData = { errors: FormError<typeof validations.changePassword>; submitted?: never };
type ChangePasswordSuccessfulActionData = { errors?: never; submitted: true };
export type ChangePasswordActionData = {
changePassword: ChangePasswordFailureActionData | ChangePasswordSuccessfulActionData;
};
async function changePassword(request: Request, formData: unknown) {
const validation = validate(validations.changePassword, formData);
if (validation.errors) {
return json<ChangePasswordActionData>({
changePassword: { errors: validation.errors },
});
}
const {
user: { id },
} = await requireLoggedIn(request);
const user = await db.user.findUnique({ where: { id } });
const { currentPassword, newPassword } = validation.data;
const verificationResult = await verifyPassword(user!.hashedPassword!, currentPassword);
if ([SecurePassword.INVALID, SecurePassword.INVALID_UNRECOGNIZED_HASH, false].includes(verificationResult)) {
return json<ChangePasswordActionData>({
changePassword: { errors: { currentPassword: "Current password is incorrect" } },
});
}
const hashedPassword = await hashPassword(newPassword.trim());
await db.user.update({
where: { id: user!.id },
data: { hashedPassword },
});
return json<ChangePasswordActionData>({
changePassword: { submitted: true },
});
}
type UpdateUserFailureActionData = { errors: FormError<typeof validations.updateUser>; submitted?: never };
type UpdateUserSuccessfulActionData = { errors?: never; submitted: true };
export type UpdateUserActionData = {
updateUser: UpdateUserFailureActionData | UpdateUserSuccessfulActionData;
};
async function updateUser(request: Request, formData: unknown) {
const validation = validate(validations.updateUser, formData);
if (validation.errors) {
return json<UpdateUserActionData>({
updateUser: { errors: validation.errors },
});
}
const { user } = await requireLoggedIn(request);
const { email, fullName } = validation.data;
await db.user.update({
where: { id: user.id },
data: { email, fullName },
});
return json<UpdateUserActionData>({
updateUser: { submitted: true },
});
}
type Action = "deleteUser" | "updateUser" | "changePassword";
const validations = {
deleteUser: null,
changePassword: z.object({
currentPassword: z.string(),
newPassword: z.string().min(10).max(100),
}),
updateUser: z.object({
fullName: z.string(),
email: z.string(),
}),
} as const;

View File

@ -5,8 +5,7 @@ import type { Prisma } from "@prisma/client";
import db from "~/utils/db.server";
import { type FormActionData, validate } from "~/utils/validation.server";
import { refreshSessionData, requireLoggedIn } from "~/utils/auth.server";
import { commitSession } from "~/utils/session.server";
import { commitSession, getSession } from "~/utils/session.server";
import setTwilioWebhooksQueue from "~/queues/set-twilio-webhooks.server";
import logger from "~/utils/logger.server";
import { encrypt } from "~/utils/encryption";
@ -40,7 +39,8 @@ const action: ActionFunction = async ({ request }) => {
export type SetPhoneNumberActionData = FormActionData<typeof validations, "setPhoneNumber">;
async function setPhoneNumber(request: Request, formData: unknown) {
const { organization, twilio } = await requireLoggedIn(request);
const session = await getSession(request);
const twilio = session.get("twilio");
if (!twilio) {
return badRequest<SetPhoneNumberActionData>({
setPhoneNumber: {
@ -72,7 +72,6 @@ async function setPhoneNumber(request: Request, formData: unknown) {
});
await setTwilioWebhooksQueue.add(`set twilio webhooks for phoneNumberId=${validation.data.phoneNumberSid}`, {
phoneNumberId: validation.data.phoneNumberSid,
organizationId: organization.id,
});
return json<SetPhoneNumberActionData>({ setPhoneNumber: { submitted: true } });
@ -81,7 +80,8 @@ async function setPhoneNumber(request: Request, formData: unknown) {
export type SetTwilioCredentialsActionData = FormActionData<typeof validations, "setTwilioCredentials">;
async function setTwilioCredentials(request: Request, formData: unknown) {
const { organization, twilio } = await requireLoggedIn(request);
const session = await getSession(request);
const twilio = session.get("twilio");
const validation = validate(validations.setTwilioCredentials, formData);
if (validation.errors) {
return badRequest<SetTwilioCredentialsActionData>({ setTwilioCredentials: { errors: validation.errors } });
@ -99,10 +99,10 @@ async function setTwilioCredentials(request: Request, formData: unknown) {
throw error;
}
let session: Session | undefined;
if (twilio) {
console.log("fail");
await db.twilioAccount.delete({ where: { accountSid: twilio?.accountSid } });
session = (await refreshSessionData(request)).session;
session.unset("twilio");
}
return json<SetTwilioCredentialsActionData>(
@ -112,11 +112,9 @@ async function setTwilioCredentials(request: Request, formData: unknown) {
},
},
{
headers: session
? {
"Set-Cookie": await commitSession(session),
}
: {},
headers: {
"Set-Cookie": await commitSession(session),
},
},
);
}
@ -128,13 +126,8 @@ async function setTwilioCredentials(request: Request, formData: unknown) {
const [phoneNumbers] = await Promise.all([
twilioClient.incomingPhoneNumbers.list(),
db.twilioAccount.upsert({
where: { organizationId: organization.id },
create: {
organization: {
connect: { id: organization.id },
},
...data,
},
where: { accountSid: twilioAccountSid },
create: data,
update: data,
}),
]);
@ -143,11 +136,11 @@ async function setTwilioCredentials(request: Request, formData: unknown) {
accountSid: twilioAccountSid,
});
await Promise.all(
phoneNumbers.map(async (phoneNumber) => {
phoneNumbers.map(async (phoneNumber, index) => {
const phoneNumberId = phoneNumber.sid;
logger.info(`Importing phone number with id=${phoneNumberId}`);
try {
await db.phoneNumber.create({
await db.phoneNumber.createMany({
data: {
id: phoneNumberId,
twilioAccountSid,
@ -156,6 +149,7 @@ async function setTwilioCredentials(request: Request, formData: unknown) {
isFetchingCalls: true,
isFetchingMessages: true,
},
skipDuplicates: true,
});
await Promise.all([
@ -177,19 +171,25 @@ async function setTwilioCredentials(request: Request, formData: unknown) {
}),
);
const { session } = await refreshSessionData(request);
session.set("twilio", { accountSid: twilioAccountSid, authToken });
console.log("{ accountSid: twilioAccountSid, authToken }", { accountSid: twilioAccountSid, authToken });
console.log("session", session.get("twilio"), session.data);
const setCookie = await commitSession(session);
console.log("set twilio in session", setCookie);
return json<SetTwilioCredentialsActionData>(
{ setTwilioCredentials: { submitted: true } },
{
headers: {
"Set-Cookie": await commitSession(session),
"Set-Cookie": setCookie,
},
},
);
}
async function refreshPhoneNumbers(request: Request) {
const { twilio } = await requireLoggedIn(request);
const session = await getSession(request);
const twilio = session.get("twilio");
if (!twilio) {
throw new Error("unreachable");
}