make non-core env variables optional

This commit is contained in:
MarconLP 2023-05-03 21:35:58 +02:00
parent e05b05f673
commit 29f2da7379
No known key found for this signature in database
GPG key ID: F4CAFFDFA3451D5E
13 changed files with 128 additions and 102 deletions

View file

@ -1,15 +1,9 @@
# Prisma
# https://www.prisma.io/docs/reference/database-reference/connection-urls#env
# Database
DATABASE_URL=""
# Next Auth
# You can generate a new secret on the command line with:
# openssl rand -base64 32
# https://next-auth.js.org/configuration/options#secret
# Authjs
NEXTAUTH_URL="http://localhost:3000/"
NEXTAUTH_SECRET=""
NEXTAUTH_URL="http://localhost:3000"
# Next Auth Provider
GOOGLE_CLIENT_ID=""
GOOGLE_CLIENT_SECRET=""
GITHUB_ID=""
@ -38,10 +32,10 @@ NEXT_PUBLIC_CRISP_WEBSITE_ID=""
# posthog
NEXT_PUBLIC_POSTHOG_KEY=""
NEXT_PUBLIC_POSTHOG_HOST=""
NEXT_PUBLIC_POSTHOG_HOST="https://app.posthog.com"
NEXT_PUBLIC_POSTHOG_PROXY_HOST="/ioafe"
POSTHOG_PROXY_PATH="ioafe"
# redis for ratelimiting
UPSTASH_REDIS_REST_URL=""
UPSTASH_REDIS_REST_TOKEN="="
UPSTASH_REDIS_REST_TOKEN=""

View file

@ -4,7 +4,9 @@ import { env } from "~/env.mjs";
export default function CrispChat() {
useEffect(() => {
Crisp.configure(env.NEXT_PUBLIC_CRISP_WEBSITE_ID);
if (env.NEXT_PUBLIC_CRISP_WEBSITE_ID) {
Crisp.configure(env.NEXT_PUBLIC_CRISP_WEBSITE_ID);
}
}, []);
return null;

View file

@ -28,13 +28,13 @@ const server = z.object({
AWS_KEY_ID: z.string(),
AWS_SECRET_ACCESS_KEY: z.string(),
AWS_BUCKET_NAME: z.string(),
STRIPE_SECRET_KEY: z.string(),
STRIPE_WEBHOOK_SECRET: z.string(),
STRIPE_MONTHLY_PRICE_ID: z.string(),
STRIPE_ANNUAL_PRICE_ID: z.string(),
STRIPE_SECRET_KEY: z.string().nullish(),
STRIPE_WEBHOOK_SECRET: z.string().nullish(),
STRIPE_MONTHLY_PRICE_ID: z.string().nullish(),
STRIPE_ANNUAL_PRICE_ID: z.string().nullish(),
POSTHOG_PROXY_PATH: z.string(),
UPSTASH_REDIS_REST_URL: z.string(),
UPSTASH_REDIS_REST_TOKEN: z.string(),
UPSTASH_REDIS_REST_URL: z.string().nullish(),
UPSTASH_REDIS_REST_TOKEN: z.string().nullish(),
});
/**
@ -43,9 +43,9 @@ const server = z.object({
*/
const client = z.object({
// NEXT_PUBLIC_CLIENTVAR: z.string().min(1),
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string(),
NEXT_PUBLIC_CRISP_WEBSITE_ID: z.string(),
NEXT_PUBLIC_POSTHOG_KEY: z.string(),
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().nullish(),
NEXT_PUBLIC_CRISP_WEBSITE_ID: z.string().nullish(),
NEXT_PUBLIC_POSTHOG_KEY: z.string().nullish(),
NEXT_PUBLIC_POSTHOG_HOST: z.string(),
NEXT_PUBLIC_POSTHOG_PROXY_HOST: z.string(),
});

View file

@ -12,12 +12,12 @@ import { env } from "~/env.mjs";
import { type ReactNode, useEffect } from "react";
// Check that PostHog is client-side (used to handle Next.js SSR)
if (typeof window !== "undefined") {
if (typeof window !== "undefined" && !!env.NEXT_PUBLIC_POSTHOG_KEY) {
posthog.init(env.NEXT_PUBLIC_POSTHOG_KEY, {
api_host: env.NEXT_PUBLIC_POSTHOG_PROXY_HOST,
// Enable debug mode in development
loaded: (posthog) => {
if (process.env.NODE_ENV === "development") posthog.debug(false);
if (process.env.NODE_ENV === "development") posthog.debug();
},
});
}
@ -47,16 +47,16 @@ const PostHogIdentificationWrapper = ({
const posthog = usePostHog();
useEffect(() => {
if (!posthog) return;
if (!posthog?.__loaded) return;
if (status === "authenticated") {
const { id, name, email, stripeSubscriptionStatus } = session?.user;
posthog.identify(id, {
posthog?.identify(id, {
name,
email,
stripeSubscriptionStatus,
});
} else if (status === "unauthenticated") {
posthog.reset();
posthog?.reset();
}
}, [posthog, session, status]);

View file

@ -24,6 +24,10 @@ export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (!webhookSecret || !stripe) {
return res.status(500).end("Stripe env variables not set");
}
if (req.method === "POST") {
const buf = await buffer(req);
const sig = req.headers["stripe-signature"];
@ -72,11 +76,11 @@ export default async function handler(
const userId = subscription.metadata.userId;
if (userId) {
posthog.capture({
posthog?.capture({
distinctId: userId,
event: "stripe invoice.payment_failed",
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
}
break;
case "customer.subscription.deleted":

View file

@ -8,6 +8,14 @@ export const stripeRouter = createTRPCRouter({
.input(z.object({ billedAnnually: z.boolean() }))
.mutation(
async ({ ctx: { prisma, stripe, session, req, posthog }, input }) => {
if (
!stripe ||
!env.STRIPE_ANNUAL_PRICE_ID ||
!env.STRIPE_MONTHLY_PRICE_ID
) {
throw new Error("Stripe env variables not set");
}
const customerId = await getOrCreateStripeCustomerIdForUser({
prisma,
stripe,
@ -50,20 +58,24 @@ export const stripeRouter = createTRPCRouter({
throw new Error("Could not create checkout session");
}
posthog.capture({
posthog?.capture({
distinctId: session.user.id,
event: "visiting checkout page",
properties: {
billingCycle: input.billedAnnually ? "annual" : "monthly",
},
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
return { checkoutUrl: checkoutSession.url };
}
),
createBillingPortalSession: protectedProcedure.mutation(
async ({ ctx: { stripe, session, prisma, req, posthog } }) => {
if (!stripe) {
throw new Error("Stripe env variables not set");
}
const customerId = await getOrCreateStripeCustomerIdForUser({
prisma,
stripe,
@ -89,14 +101,14 @@ export const stripeRouter = createTRPCRouter({
throw new Error("Could not create billing portal session");
}
posthog.capture({
posthog?.capture({
distinctId: session.user.id,
event: "visiting billing portal page",
properties: {
stripeSubscriptionStatus: session.user.stripeSubscriptionStatus,
},
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
return { billingPortalUrl: stripeBillingPortalSession.url };
}

View file

@ -23,14 +23,14 @@ export const videoRouter = createTRPCRouter({
},
});
posthog.capture({
posthog?.capture({
distinctId: session.user.id,
event: "viewing video list",
properties: {
videoAmount: videos.length,
},
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
const videosWithThumbnailUrl = await Promise.all(
videos.map(async (video) => {
@ -70,7 +70,7 @@ export const videoRouter = createTRPCRouter({
}
if (session) {
posthog.capture({
posthog?.capture({
distinctId: session.user.id,
event: "viewing video",
properties: {
@ -83,7 +83,7 @@ export const videoRouter = createTRPCRouter({
videoShareLinkExpiresAt: video.shareLinkExpiresAt,
},
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
}
const getObjectCommand = new GetObjectCommand({
@ -110,7 +110,7 @@ export const videoRouter = createTRPCRouter({
videos.length >= 10 &&
session.user.stripeSubscriptionStatus !== "active"
) {
posthog.capture({
posthog?.capture({
distinctId: session.user.id,
event: "hit video upload limit",
properties: {
@ -118,7 +118,7 @@ export const videoRouter = createTRPCRouter({
stripeSubscriptionStatus: session.user.stripeSubscriptionStatus,
},
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
throw new TRPCError({
code: "FORBIDDEN",
@ -127,7 +127,7 @@ export const videoRouter = createTRPCRouter({
});
}
posthog.capture({
posthog?.capture({
distinctId: session.user.id,
event: "uploading video",
properties: {
@ -135,7 +135,7 @@ export const videoRouter = createTRPCRouter({
stripeSubscriptionStatus: session.user.stripeSubscriptionStatus,
},
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
const video = await prisma.video.create({
data: {
@ -184,7 +184,7 @@ export const videoRouter = createTRPCRouter({
throw new TRPCError({ code: "FORBIDDEN" });
}
posthog.capture({
posthog?.capture({
distinctId: session.user.id,
event: "update video setSharing",
properties: {
@ -192,7 +192,7 @@ export const videoRouter = createTRPCRouter({
videoSharing: input.sharing,
},
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
return {
success: true,
@ -218,7 +218,7 @@ export const videoRouter = createTRPCRouter({
throw new TRPCError({ code: "FORBIDDEN" });
}
posthog.capture({
posthog?.capture({
distinctId: session.user.id,
event: "update video delete_after_link_expires",
properties: {
@ -226,7 +226,7 @@ export const videoRouter = createTRPCRouter({
delete_after_link_expires: input.delete_after_link_expires,
},
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
return {
success: true,
@ -255,7 +255,7 @@ export const videoRouter = createTRPCRouter({
throw new TRPCError({ code: "FORBIDDEN" });
}
posthog.capture({
posthog?.capture({
distinctId: session.user.id,
event: "update video shareLinkExpiresAt",
properties: {
@ -263,7 +263,7 @@ export const videoRouter = createTRPCRouter({
shareLinkExpiresAt: input.shareLinkExpiresAt,
},
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
return {
success: true,
@ -292,7 +292,7 @@ export const videoRouter = createTRPCRouter({
throw new TRPCError({ code: "FORBIDDEN" });
}
posthog.capture({
posthog?.capture({
distinctId: session.user.id,
event: "update video title",
properties: {
@ -300,7 +300,7 @@ export const videoRouter = createTRPCRouter({
title: input.title,
},
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
return {
success: true,
@ -325,14 +325,14 @@ export const videoRouter = createTRPCRouter({
throw new TRPCError({ code: "FORBIDDEN" });
}
posthog.capture({
posthog?.capture({
distinctId: session.user.id,
event: "video delete",
properties: {
videoId: input.videoId,
},
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
const deleteVideoObject = await s3.send(
new DeleteObjectCommand({

View file

@ -125,9 +125,11 @@ const enforceUserIsAuthed = t.middleware(async ({ ctx, next }) => {
if (!ctx.session || !ctx.session.user) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
const { success } = await rateLimit.limit(ctx.session.user.id);
if (!success) {
throw new TRPCError({ code: "TOO_MANY_REQUESTS" });
if (rateLimit) {
const { success } = await rateLimit.limit(ctx.session.user.id);
if (!success) {
throw new TRPCError({ code: "TOO_MANY_REQUESTS" });
}
}
return next({

View file

@ -70,40 +70,44 @@ export const authOptions: NextAuthOptions = {
],
events: {
async signIn(message) {
const client = new PostHog(env.NEXT_PUBLIC_POSTHOG_KEY, {
host: env.NEXT_PUBLIC_POSTHOG_HOST,
});
if (!!env.NEXT_PUBLIC_POSTHOG_KEY) {
const client = new PostHog(env.NEXT_PUBLIC_POSTHOG_KEY, {
host: env.NEXT_PUBLIC_POSTHOG_HOST,
});
client.capture({
distinctId: message.user.id,
event: "user logged in",
properties: {
provider: message.account?.provider,
isNewUser: message.isNewUser,
},
});
client.capture({
distinctId: message.user.id,
event: "user logged in",
properties: {
provider: message.account?.provider,
isNewUser: message.isNewUser,
},
});
await client.shutdownAsync();
await client.shutdownAsync();
}
},
async signOut(message) {
const session = message.session as unknown as {
id: string;
sessionToken: string;
userId: string;
expires: Date;
};
if (!session?.userId) return;
if (!!env.NEXT_PUBLIC_POSTHOG_KEY) {
const session = message.session as unknown as {
id: string;
sessionToken: string;
userId: string;
expires: Date;
};
if (!session?.userId) return;
const client = new PostHog(env.NEXT_PUBLIC_POSTHOG_KEY, {
host: env.NEXT_PUBLIC_POSTHOG_HOST,
});
const client = new PostHog(env.NEXT_PUBLIC_POSTHOG_KEY, {
host: env.NEXT_PUBLIC_POSTHOG_HOST,
});
client.capture({
distinctId: session.userId,
event: "user logged out",
});
client.capture({
distinctId: session.userId,
event: "user logged out",
});
await client.shutdownAsync();
await client.shutdownAsync();
}
},
},
pages: {

View file

@ -1,6 +1,8 @@
import { PostHog } from "posthog-node";
import { env } from "~/env.mjs";
export const posthog = new PostHog(env.NEXT_PUBLIC_POSTHOG_KEY, {
host: env.NEXT_PUBLIC_POSTHOG_HOST,
});
export const posthog = !!env.NEXT_PUBLIC_POSTHOG_KEY
? new PostHog(env.NEXT_PUBLIC_POSTHOG_KEY, {
host: env.NEXT_PUBLIC_POSTHOG_HOST,
})
: null;

View file

@ -2,10 +2,13 @@ import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
import { env } from "~/env.mjs";
export const rateLimit = new Ratelimit({
redis: new Redis({
url: env.UPSTASH_REDIS_REST_URL,
token: env.UPSTASH_REDIS_REST_TOKEN,
}),
limiter: Ratelimit.slidingWindow(60, "60 s"),
});
export const rateLimit =
!!env.UPSTASH_REDIS_REST_URL && !!env.UPSTASH_REDIS_REST_TOKEN
? new Ratelimit({
redis: new Redis({
url: env.UPSTASH_REDIS_REST_URL,
token: env.UPSTASH_REDIS_REST_TOKEN,
}),
limiter: Ratelimit.slidingWindow(60, "60 s"),
})
: null;

View file

@ -60,7 +60,7 @@ export const handleInvoicePaid = async ({
event: Stripe.Event;
stripe: Stripe;
prisma: PrismaClient;
posthog: PostHog;
posthog: PostHog | null;
}) => {
const invoice = event.data.object as Stripe.Invoice;
const subscriptionId = invoice.subscription;
@ -81,14 +81,14 @@ export const handleInvoicePaid = async ({
});
if (userId && subscription.status) {
posthog.capture({
posthog?.capture({
distinctId: userId,
event: "stripe invoice.paid",
properties: {
stripeSubscriptionStatus: subscription.status,
},
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
}
};
@ -99,7 +99,7 @@ export const handleSubscriptionCreatedOrUpdated = async ({
}: {
event: Stripe.Event;
prisma: PrismaClient;
posthog: PostHog;
posthog: PostHog | null;
}) => {
const subscription = event.data.object as Stripe.Subscription;
const userId = subscription.metadata.userId;
@ -116,11 +116,11 @@ export const handleSubscriptionCreatedOrUpdated = async ({
});
if (userId && subscription.status) {
posthog.capture({
posthog?.capture({
distinctId: userId,
event: "stripe subscription created or updated",
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
}
};
@ -131,7 +131,7 @@ export const handleSubscriptionCanceled = async ({
}: {
event: Stripe.Event;
prisma: PrismaClient;
posthog: PostHog;
posthog: PostHog | null;
}) => {
const subscription = event.data.object as Stripe.Subscription;
const userId = subscription.metadata.userId;
@ -148,10 +148,10 @@ export const handleSubscriptionCanceled = async ({
});
if (userId && subscription.status) {
posthog.capture({
posthog?.capture({
distinctId: userId,
event: "stripe subscription cancelled",
});
void posthog.shutdownAsync();
void posthog?.shutdownAsync();
}
};

View file

@ -1,5 +1,8 @@
import Stripe from "stripe";
import { env } from "~/env.mjs";
export const stripe = new Stripe(env.STRIPE_SECRET_KEY, {
apiVersion: "2022-11-15",
});
export const stripe = !!env.STRIPE_SECRET_KEY
? new Stripe(env.STRIPE_SECRET_KEY, {
apiVersion: "2022-11-15",
})
: null;