build pass
This commit is contained in:
parent
9f5e646338
commit
3762305c4f
@ -7,9 +7,6 @@ import listen from "test-listen";
|
||||
import fetch from "isomorphic-fetch";
|
||||
import crypto from "crypto";
|
||||
|
||||
import CookieStore from "../lib/cookie-store";
|
||||
import Session from "../lib/session";
|
||||
|
||||
type Authentication =
|
||||
| "none"
|
||||
| "auth0"
|
||||
@ -93,15 +90,13 @@ function writeSessionToCookie(
|
||||
res: ServerResponse,
|
||||
authentication: Authentication,
|
||||
) {
|
||||
const cookieStore = new CookieStore();
|
||||
const session: Session = new Session({
|
||||
const session = {
|
||||
id: `${authentication}|userId`,
|
||||
email: "test@fss.test",
|
||||
name: "Groot",
|
||||
teamId: "teamId",
|
||||
role: "owner",
|
||||
});
|
||||
cookieStore.save(req, res, session);
|
||||
};
|
||||
|
||||
const setCookieHeader = res.getHeader("Set-Cookie") as string[];
|
||||
// write it to request headers to immediately have access to the user's session
|
||||
|
@ -1,36 +1,28 @@
|
||||
jest.mock("../../../../pages/api/user/_auth0", () => ({
|
||||
setAppMetadata: jest.fn(),
|
||||
}));
|
||||
jest.mock("../../../../pages/api/_send-email", () => ({
|
||||
sendEmail: jest.fn(),
|
||||
}));
|
||||
jest.mock("../../../../database/users", () => ({ createUser: jest.fn() }));
|
||||
jest.mock("../../../../database/teams", () => ({ createTeam: jest.fn() }));
|
||||
jest.mock("../../../../database/customer", () => ({ createCustomer: jest.fn() }));
|
||||
|
||||
import { parse } from "set-cookie-parser";
|
||||
|
||||
import { callApiHandler } from "../../../../../jest/helpers";
|
||||
import signUpHandler from "../../../../pages/api/auth/sign-up";
|
||||
import { sessionName } from "../../../../../lib/cookie-store";
|
||||
import { sendEmail } from "../../../../pages/api/_send-email";
|
||||
import { createUser } from "../../../../database/users";
|
||||
import { createTeam } from "../../../../database/teams";
|
||||
import { createCustomer } from "../../../../database/customer";
|
||||
|
||||
const sessionName = "";
|
||||
|
||||
describe("/api/auth/sign-up", () => {
|
||||
const mockedSendEmail = sendEmail as jest.Mock<
|
||||
ReturnType<typeof sendEmail>
|
||||
>;
|
||||
const mockedCreateUser = createUser as jest.Mock<
|
||||
ReturnType<typeof createUser>
|
||||
>;
|
||||
const mockedCreateTeam = createTeam as jest.Mock<
|
||||
ReturnType<typeof createTeam>
|
||||
const mockedCreateCustomer = createCustomer as jest.Mock<
|
||||
ReturnType<typeof createCustomer>
|
||||
>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockedSendEmail.mockClear();
|
||||
mockedCreateUser.mockClear();
|
||||
mockedCreateTeam.mockClear();
|
||||
mockedCreateCustomer.mockClear();
|
||||
});
|
||||
|
||||
test("responds 405 to GET", async () => {
|
||||
@ -46,22 +38,11 @@ describe("/api/auth/sign-up", () => {
|
||||
});
|
||||
|
||||
test("responds 200 to POST with body from email login", async () => {
|
||||
mockedCreateUser.mockResolvedValue({
|
||||
/*mockedCreateCustomer.mockResolvedValue({
|
||||
id: "auth0|1234567",
|
||||
teamId: "98765",
|
||||
role: "owner",
|
||||
email: "test@fss.dev",
|
||||
name: "Groot",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
mockedCreateTeam.mockResolvedValue({
|
||||
id: "98765",
|
||||
subscriptionId: null,
|
||||
teamMembersLimit: 1,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
});*/
|
||||
|
||||
const body = {
|
||||
accessToken:
|
||||
|
@ -1,15 +0,0 @@
|
||||
import { FunctionComponent } from "react";
|
||||
import usePress from "react-gui/use-press";
|
||||
|
||||
const LongPressHandler: FunctionComponent = ({ children }) => {
|
||||
const onLongPress = (event: any) => console.log("event", event);
|
||||
const ref = usePress({ onLongPress });
|
||||
|
||||
return (
|
||||
<div ref={ref} onContextMenu={e => e.preventDefault()}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LongPressHandler;
|
@ -1,215 +0,0 @@
|
||||
import type { FunctionComponent } from "react";
|
||||
import { useState } from "react";
|
||||
import clsx from "clsx";
|
||||
import { CheckIcon } from "@heroicons/react/outline";
|
||||
|
||||
import useUser from "../../hooks/use-user";
|
||||
import useSubscription from "../../hooks/use-subscription";
|
||||
|
||||
import type { Plan, PlanId } from "../../subscription/plans";
|
||||
import {
|
||||
FREE,
|
||||
MONTHLY,
|
||||
ANNUALLY,
|
||||
TEAM_MONTHLY,
|
||||
TEAM_ANNUALLY,
|
||||
} from "../../subscription/plans";
|
||||
|
||||
type Props = {
|
||||
activePlanId?: PlanId;
|
||||
};
|
||||
const PLANS: Record<BillingSchedule, Plan[]> = {
|
||||
monthly: [FREE, MONTHLY, TEAM_MONTHLY],
|
||||
yearly: [FREE, ANNUALLY, TEAM_ANNUALLY],
|
||||
};
|
||||
|
||||
const PricingPlans: FunctionComponent<Props> = ({ activePlanId }) => {
|
||||
const [billingSchedule, setBillingSchedule] = useState<BillingSchedule>(
|
||||
"monthly",
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||
<div className="sm:flex sm:flex-col sm:align-center">
|
||||
<div className="relative self-center mt-6 bg-gray-100 rounded-lg p-0.5 flex sm:mt-8">
|
||||
<button
|
||||
onClick={() => setBillingSchedule("monthly")}
|
||||
type="button"
|
||||
className={clsx(
|
||||
"relative w-1/2 border-gray-200 rounded-md shadow-sm py-2 text-sm font-medium text-gray-700 whitespace-nowrap focus:outline-none focus:ring-2 focus:ring-primary-500 focus:z-10 sm:w-auto sm:px-8",
|
||||
{
|
||||
"bg-white": billingSchedule === "monthly",
|
||||
},
|
||||
)}
|
||||
>
|
||||
Monthly billing
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setBillingSchedule("yearly")}
|
||||
type="button"
|
||||
className={clsx(
|
||||
"ml-0.5 relative w-1/2 border border-transparent rounded-md py-2 text-sm font-medium text-gray-700 whitespace-nowrap focus:outline-none focus:ring-2 focus:ring-primary-500 focus:z-10 sm:w-auto sm:px-8",
|
||||
{
|
||||
"bg-white": billingSchedule === "yearly",
|
||||
},
|
||||
)}
|
||||
>
|
||||
Yearly billing
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 space-y-4 flex flex-row flex-wrap sm:mt-10 sm:space-y-0 sm:gap-6 lg:max-w-4xl lg:mx-auto">
|
||||
{PLANS[billingSchedule].map((plan) => (
|
||||
<PricingPlan
|
||||
key={`pricing-plan-${plan.name}`}
|
||||
plan={plan}
|
||||
billingSchedule={billingSchedule}
|
||||
activePlanId={activePlanId}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PricingPlans;
|
||||
|
||||
type BillingSchedule = "yearly" | "monthly";
|
||||
|
||||
type PricingPlanProps = {
|
||||
plan: Plan;
|
||||
billingSchedule: BillingSchedule;
|
||||
activePlanId?: PlanId;
|
||||
};
|
||||
|
||||
const PricingPlan: FunctionComponent<PricingPlanProps> = ({
|
||||
plan,
|
||||
billingSchedule,
|
||||
activePlanId,
|
||||
}) => {
|
||||
const { subscribe, changePlan } = useSubscription();
|
||||
const { userProfile } = useUser();
|
||||
const { name, description, features, price, id } = plan;
|
||||
const isActivePlan =
|
||||
(typeof activePlanId !== "undefined" ? activePlanId : "free") === id;
|
||||
|
||||
function movePlan() {
|
||||
const teamId = userProfile!.teamId;
|
||||
const email = userProfile!.email;
|
||||
const planId = plan.id;
|
||||
|
||||
if (typeof activePlanId === "undefined" && typeof planId === "number") {
|
||||
return subscribe({ email, teamId, planId });
|
||||
}
|
||||
|
||||
changePlan({ planId });
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"bg-white w-full flex-grow border rounded-lg shadow-sm divide-y divide-gray-200 sm:w-auto",
|
||||
{
|
||||
"border-gray-200": !isActivePlan,
|
||||
"border-primary-600": isActivePlan,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div className="p-6">
|
||||
<h2 className="text-lg leading-6 font-medium text-gray-900">
|
||||
{name}
|
||||
</h2>
|
||||
<p className="mt-4 text-sm text-gray-500">{description}</p>
|
||||
<p className="mt-8">
|
||||
<PlanPrice
|
||||
price={price}
|
||||
billingSchedule={billingSchedule}
|
||||
/>
|
||||
</p>
|
||||
|
||||
<div className="mt-8">
|
||||
<PlanButton
|
||||
name={name}
|
||||
isActivePlan={isActivePlan}
|
||||
changePlan={movePlan}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pt-6 pb-8 px-6">
|
||||
<h3 className="text-xs font-medium text-gray-900 tracking-wide uppercase">
|
||||
What's included
|
||||
</h3>
|
||||
<ul className="mt-6 space-y-4">
|
||||
{features.map((feature) => (
|
||||
<li
|
||||
key={`pricing-plan-${name}-feature-${feature}`}
|
||||
className="flex space-x-3"
|
||||
>
|
||||
<CheckIcon className="flex-shrink-0 h-5 w-5 text-green-500" />
|
||||
<span className="text-sm text-gray-500">
|
||||
{feature}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type PlanButtonProps = {
|
||||
name: Plan["name"];
|
||||
isActivePlan: boolean;
|
||||
changePlan: () => void;
|
||||
};
|
||||
|
||||
const PlanButton: FunctionComponent<PlanButtonProps> = ({
|
||||
name,
|
||||
isActivePlan,
|
||||
changePlan,
|
||||
}) => {
|
||||
return isActivePlan ? (
|
||||
<div className="block w-full py-2 text-sm font-semibold text-gray-500 text-center">
|
||||
You're currently on this plan
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={changePlan}
|
||||
className="block w-full cursor-pointer bg-primary-600 border border-primary-600 rounded-md py-2 text-sm font-semibold text-white text-center hover:bg-primary-700"
|
||||
>
|
||||
Move to <span className="font-bold">{name}</span> plan
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
type PlanPriceProps = {
|
||||
price: Plan["price"];
|
||||
billingSchedule: BillingSchedule;
|
||||
};
|
||||
|
||||
const PlanPrice: FunctionComponent<PlanPriceProps> = ({
|
||||
price,
|
||||
billingSchedule,
|
||||
}) => {
|
||||
if (price === "free") {
|
||||
return (
|
||||
<span className="text-4xl font-extrabold text-gray-900">Free</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className="text-4xl font-extrabold text-gray-900">
|
||||
${price}
|
||||
</span>
|
||||
<span className="text-base font-medium text-gray-500">/mo</span>
|
||||
{billingSchedule === "yearly" ? (
|
||||
<span className="ml-1 text-sm text-gray-500">
|
||||
billed yearly
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
@ -30,7 +30,7 @@ const ProfileInformations: FunctionComponent = () => {
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setValue("name", user.userProfile?.name ?? "");
|
||||
setValue("name", user.userProfile?.user_metadata.name ?? "");
|
||||
setValue("email", user.userProfile?.email ?? "");
|
||||
}, [setValue, user.userProfile]);
|
||||
|
||||
@ -40,7 +40,7 @@ const ProfileInformations: FunctionComponent = () => {
|
||||
}
|
||||
|
||||
try {
|
||||
await user.updateUser({ name, email });
|
||||
await user.updateUser({ email, data: { name } });
|
||||
} catch (error) {
|
||||
logger.error(error.response, "error updating user infos");
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import type { PlanId } from "../subscription/plans";
|
||||
import appLogger from "../../lib/logger";
|
||||
import supabase from "../supabase/server";
|
||||
|
||||
const logger = appLogger.child({ module: "subscriptions" });
|
||||
|
||||
@ -32,12 +33,6 @@ export type Subscription = {
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
type FirestoreSubscription = FirestoreEntry<Subscription>;
|
||||
|
||||
const subscriptions = firestoreCollection<FirestoreSubscription>(
|
||||
"subscriptions",
|
||||
);
|
||||
|
||||
type CreateSubscriptionParams = Pick<
|
||||
Subscription,
|
||||
| "userId"
|
||||
@ -62,24 +57,25 @@ export async function createSubscription({
|
||||
cancelUrl,
|
||||
lastEventTime,
|
||||
}: CreateSubscriptionParams): Promise<Subscription> {
|
||||
const createdAt = FieldValue.serverTimestamp() as Timestamp;
|
||||
await subscriptions.doc(paddleSubscriptionId).set({
|
||||
userId,
|
||||
planId,
|
||||
paddleCheckoutId,
|
||||
paddleSubscriptionId,
|
||||
nextBillDate,
|
||||
status,
|
||||
updateUrl,
|
||||
cancelUrl,
|
||||
lastEventTime,
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
});
|
||||
const createdAt = new Date();
|
||||
const { data } = await supabase
|
||||
.from<Subscription>("subscription")
|
||||
.insert({
|
||||
userId,
|
||||
planId,
|
||||
paddleCheckoutId,
|
||||
paddleSubscriptionId,
|
||||
nextBillDate,
|
||||
status,
|
||||
updateUrl,
|
||||
cancelUrl,
|
||||
lastEventTime,
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
})
|
||||
.throwOnError();
|
||||
|
||||
const subscription = await findSubscription({ paddleSubscriptionId });
|
||||
|
||||
return subscription!;
|
||||
return data![0];
|
||||
}
|
||||
|
||||
type GetSubscriptionParams = Pick<Subscription, "paddleSubscriptionId">;
|
||||
@ -87,14 +83,15 @@ type GetSubscriptionParams = Pick<Subscription, "paddleSubscriptionId">;
|
||||
export async function findSubscription({
|
||||
paddleSubscriptionId,
|
||||
}: GetSubscriptionParams): Promise<Subscription | undefined> {
|
||||
const subscriptionDocument = await subscriptions
|
||||
.doc(paddleSubscriptionId)
|
||||
.get();
|
||||
if (!subscriptionDocument.exists) {
|
||||
return;
|
||||
}
|
||||
const { error, data } = await supabase
|
||||
.from<Subscription>("subscription")
|
||||
.select("*")
|
||||
.eq("paddleSubscriptionId", paddleSubscriptionId)
|
||||
.single();
|
||||
|
||||
return convertFromFirestore(subscriptionDocument.data()!);
|
||||
if (error) throw error;
|
||||
|
||||
return data!;
|
||||
}
|
||||
|
||||
type FindUserSubscriptionParams = Pick<Subscription, "userId">;
|
||||
@ -102,18 +99,16 @@ type FindUserSubscriptionParams = Pick<Subscription, "userId">;
|
||||
export async function findUserSubscription({
|
||||
userId,
|
||||
}: FindUserSubscriptionParams): Promise<Subscription | null> {
|
||||
const subscriptionDocumentsSnapshot = await subscriptions
|
||||
.where("userId", "==", userId)
|
||||
.where("status", "!=", "deleted")
|
||||
.get();
|
||||
if (subscriptionDocumentsSnapshot.docs.length === 0) {
|
||||
logger.warn(`No subscription found for user ${userId}`);
|
||||
return null;
|
||||
}
|
||||
const { error, data } = await supabase
|
||||
.from<Subscription>("subscription")
|
||||
.select("*")
|
||||
.eq("userId", userId)
|
||||
.neq("status", "deleted")
|
||||
.single();
|
||||
|
||||
const subscriptionDocument = subscriptionDocumentsSnapshot.docs[0].data();
|
||||
if (error) throw error;
|
||||
|
||||
return convertFromFirestore(subscriptionDocument);
|
||||
return data!;
|
||||
}
|
||||
|
||||
type UpdateSubscriptionParams = Pick<Subscription, "paddleSubscriptionId"> &
|
||||
@ -135,17 +130,22 @@ export async function updateSubscription(
|
||||
update: UpdateSubscriptionParams,
|
||||
): Promise<void> {
|
||||
const paddleSubscriptionId = update.paddleSubscriptionId;
|
||||
await subscriptions.doc(paddleSubscriptionId).set(
|
||||
{
|
||||
await supabase
|
||||
.from<Subscription>("subscription")
|
||||
.update({
|
||||
...update,
|
||||
updatedAt: FieldValue.serverTimestamp() as Timestamp,
|
||||
},
|
||||
{ merge: true },
|
||||
);
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.eq("paddleSubscriptionId", paddleSubscriptionId)
|
||||
.throwOnError();
|
||||
}
|
||||
|
||||
export async function deleteSubscription({
|
||||
paddleSubscriptionId,
|
||||
}: Pick<Subscription, "paddleSubscriptionId">): Promise<void> {
|
||||
await subscriptions.doc(paddleSubscriptionId).delete();
|
||||
await supabase
|
||||
.from<Subscription>("subscription")
|
||||
.delete()
|
||||
.eq("paddleSubscriptionId", paddleSubscriptionId)
|
||||
.throwOnError();
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import {
|
||||
SUBSCRIPTION_STATUSES,
|
||||
updateSubscription,
|
||||
} from "../../../database/subscriptions";
|
||||
import { FREE } from "../../../subscription/plans";
|
||||
import appLogger from "../../../../lib/logger";
|
||||
|
||||
const logger = appLogger.child({ module: "subscription-cancelled" });
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
import { sendEmail } from "../_send-email";
|
||||
import type { ApiError } from "../_types";
|
||||
import appLogger from "../../../../lib/logger";
|
||||
import { PAID_PLANS } from "../../../subscription/plans";
|
||||
import { PLANS } from "../../../subscription/plans";
|
||||
|
||||
const logger = appLogger.child({ module: "subscription-created" });
|
||||
|
||||
@ -94,7 +94,7 @@ export async function subscriptionCreatedHandler(
|
||||
cancelUrl,
|
||||
});
|
||||
|
||||
const nextPlan = PAID_PLANS[planId];
|
||||
const nextPlan = PLANS[planId];
|
||||
sendEmail({
|
||||
subject: "Thanks for your purchase",
|
||||
body: `Welcome to ${nextPlan.name} plan`,
|
||||
|
@ -8,9 +8,10 @@ import {
|
||||
SUBSCRIPTION_STATUSES,
|
||||
updateSubscription,
|
||||
} from "../../../database/subscriptions";
|
||||
import { PAID_PLANS } from "../../../subscription/plans";
|
||||
import { PLANS } from "../../../subscription/plans";
|
||||
import appLogger from "../../../../lib/logger";
|
||||
import { sendEmail } from "../_send-email";
|
||||
import { findCustomer } from "../../../database/customer";
|
||||
|
||||
const logger = appLogger.child({ module: "subscription-updated" });
|
||||
|
||||
@ -69,7 +70,7 @@ export async function subscriptionUpdated(
|
||||
const updateUrl = body.update_url;
|
||||
const cancelUrl = body.cancel_url;
|
||||
const planId = body.subscription_plan_id;
|
||||
const nextPlan = PAID_PLANS[planId];
|
||||
const nextPlan = PLANS[planId];
|
||||
await updateSubscription({
|
||||
paddleSubscriptionId,
|
||||
planId,
|
||||
@ -79,7 +80,7 @@ export async function subscriptionUpdated(
|
||||
cancelUrl,
|
||||
});
|
||||
|
||||
const user = await findUser({ id: subscription.userId });
|
||||
const user = await findCustomer(subscription.userId);
|
||||
|
||||
sendEmail({
|
||||
subject: "Thanks for your purchase",
|
||||
|
@ -24,7 +24,7 @@ const bodySchema = Joi.object<Body>({
|
||||
export default withApiAuthRequired<Response>(async function updateSubscription(
|
||||
req,
|
||||
res,
|
||||
session,
|
||||
user,
|
||||
) {
|
||||
if (req.method !== "POST") {
|
||||
const statusCode = 405;
|
||||
@ -57,7 +57,7 @@ export default withApiAuthRequired<Response>(async function updateSubscription(
|
||||
const { planId }: Body = validationResult.value;
|
||||
|
||||
const subscription = await findUserSubscription({
|
||||
teamId: session.user.teamId,
|
||||
userId: user.id,
|
||||
});
|
||||
if (!subscription) {
|
||||
const statusCode = 500;
|
||||
|
@ -4,6 +4,7 @@ import { deleteSubscription } from "../../../database/subscriptions";
|
||||
import { cancelPaddleSubscription } from "../../../subscription/_paddle-api";
|
||||
|
||||
import appLogger from "../../../../lib/logger";
|
||||
import supabase from "../../../supabase/server";
|
||||
|
||||
type Response = void | ApiError;
|
||||
|
||||
@ -12,7 +13,7 @@ const logger = appLogger.child({ route: "/api/user/delete-user" });
|
||||
export default withApiAuthRequired<Response>(async function deleteUserHandler(
|
||||
req,
|
||||
res,
|
||||
session,
|
||||
user,
|
||||
) {
|
||||
if (req.method !== "POST") {
|
||||
const statusCode = 405;
|
||||
@ -27,34 +28,12 @@ export default withApiAuthRequired<Response>(async function deleteUserHandler(
|
||||
return;
|
||||
}
|
||||
|
||||
const { id: userId, role, teamId } = session.user;
|
||||
const team = await findTeam({ id: teamId });
|
||||
const subscriptionId = team!.subscriptionId;
|
||||
|
||||
try {
|
||||
let actions: Promise<any>[] = [
|
||||
deleteAuth0User({ id: userId }),
|
||||
deleteUser({ id: userId, teamId }),
|
||||
supabase.auth.api.deleteUser(user.id, ""),
|
||||
];
|
||||
|
||||
if (role === "owner") {
|
||||
const teamMembers = await findUsersByTeam({ teamId });
|
||||
|
||||
teamMembers.forEach((member) =>
|
||||
actions.push(deleteUser({ id: member.id, teamId })),
|
||||
);
|
||||
actions.push(deleteTeam({ id: teamId }));
|
||||
|
||||
if (subscriptionId) {
|
||||
actions.push(
|
||||
cancelPaddleSubscription({ subscriptionId }),
|
||||
deleteSubscription({
|
||||
paddleSubscriptionId: subscriptionId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: delete user phone number/messages
|
||||
await Promise.all(actions);
|
||||
|
||||
res.status(200).end();
|
||||
|
@ -1,30 +0,0 @@
|
||||
import type { ApiError } from "../_types";
|
||||
import type Session from "../../../../lib/session";
|
||||
import {
|
||||
sessionCache,
|
||||
withApiAuthRequired,
|
||||
} from "../../../../lib/session-helpers";
|
||||
import appLogger from "../../../../lib/logger";
|
||||
|
||||
type Response = Session | ApiError;
|
||||
|
||||
const logger = appLogger.child({ route: "/api/user/session" });
|
||||
|
||||
export default withApiAuthRequired<Response>(async function session(req, res) {
|
||||
if (req.method !== "GET") {
|
||||
const statusCode = 405;
|
||||
const apiError: ApiError = {
|
||||
statusCode,
|
||||
errorMessage: `Method ${req.method} Not Allowed`,
|
||||
};
|
||||
logger.error(apiError);
|
||||
|
||||
res.setHeader("Allow", ["GET"]);
|
||||
res.status(statusCode).send(apiError);
|
||||
return;
|
||||
}
|
||||
|
||||
const session = sessionCache.get(req, res)!;
|
||||
|
||||
res.status(200).send(session);
|
||||
});
|
Loading…
Reference in New Issue
Block a user