make app usable without account, remove extra stuff
This commit is contained in:
@ -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;
|
@ -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");
|
||||
}
|
||||
|
Reference in New Issue
Block a user