From f2cf0be58c2c3254ec66e3775a4a8693a0aa28f1 Mon Sep 17 00:00:00 2001 From: Florian Hochreiter Date: Sat, 11 Nov 2023 12:44:27 +0100 Subject: [PATCH 01/10] wip --- .env.example | 1 + config/interfaces/StripeConfig.ts | 1 + config/loadConfig.ts | 1 + .../frontend/v0.1/stripe/checkoutSession.ts | 17 +- .../frontend/v0.1/stripe/customerPortal.ts | 7 + pages/api/frontend/v0.1/stripe/products.ts | 9 ++ .../v0.1/stripe/subscriptions/[orgId].ts | 9 ++ pages/api/usageReport.ts | 10 +- pages/api/v0.1/messages.ts | 148 +++++++++--------- pages/api/webhooks/stripe.ts | 7 + pages/orgs/[orgId]/settings.tsx | 12 +- 11 files changed, 139 insertions(+), 83 deletions(-) diff --git a/.env.example b/.env.example index 8421637b3..f0a6b7b0d 100644 --- a/.env.example +++ b/.env.example @@ -19,6 +19,7 @@ SMTP_FROM_EMAIL_ADDRESS="flo@kula.app" SMTP_FROM_NAME="Flo Ho" # add your stripe API key here +STRIPE_ENABLED=false STRIPE_WEBHOOK_SECRET= STRIPE_SECRET_KEY= STRIPE_USE_AUTOMATIC_TAX= diff --git a/config/interfaces/StripeConfig.ts b/config/interfaces/StripeConfig.ts index 34380f548..583ad0799 100644 --- a/config/interfaces/StripeConfig.ts +++ b/config/interfaces/StripeConfig.ts @@ -1,4 +1,5 @@ export interface StripeConfig { + isEnabled: boolean; apiVersion: string; webhookSecret: string; secretKey: string; diff --git a/config/loadConfig.ts b/config/loadConfig.ts index 8bd0e326a..353c7f0b1 100644 --- a/config/loadConfig.ts +++ b/config/loadConfig.ts @@ -107,6 +107,7 @@ export function loadConfig(): Config { sentinelPassword: env.REDIS_SENTINEL_PASSWORD, }, stripeConfig: { + isEnabled: parseBooleanEnvValue(env.STRIPE_ENABLED) ?? false, apiVersion: env.STRIPE_API_VERSION || "", webhookSecret: env.STRIPE_WEBHOOK_SECRET || "", secretKey: env.STRIPE_SECRET_KEY || "", diff --git a/pages/api/frontend/v0.1/stripe/checkoutSession.ts b/pages/api/frontend/v0.1/stripe/checkoutSession.ts index f7d73b9c1..ce1ff886c 100644 --- a/pages/api/frontend/v0.1/stripe/checkoutSession.ts +++ b/pages/api/frontend/v0.1/stripe/checkoutSession.ts @@ -12,8 +12,15 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse ) { - const stripeConfig = loadConfig().server.stripeConfig; const logger = new Logger(__filename); + const stripeConfig = loadConfig().server.stripeConfig; + + if (!stripeConfig.isEnabled) { + logger.error("stripe is disabled but endpoint has been called"); + return res + .status(StatusCodes.NOT_FOUND) + .json({ message: "Endpoint not found" }); + } const userInOrg = await getUserWithRoleFromRequest(req, res); @@ -44,11 +51,9 @@ export default async function handler( if (!org) { logger.error(`No organisation found with id ${userInOrg.orgId}`); - return res - .status(StatusCodes.NOT_FOUND) - .json({ - message: `No organisation found with id ${userInOrg.orgId}`, - }); + return res.status(StatusCodes.NOT_FOUND).json({ + message: `No organisation found with id ${userInOrg.orgId}`, + }); } if (!req.body.products || !Array.isArray(req.body.products)) { diff --git a/pages/api/frontend/v0.1/stripe/customerPortal.ts b/pages/api/frontend/v0.1/stripe/customerPortal.ts index c7da8d56d..b2de7fc66 100644 --- a/pages/api/frontend/v0.1/stripe/customerPortal.ts +++ b/pages/api/frontend/v0.1/stripe/customerPortal.ts @@ -14,6 +14,13 @@ export default async function handler( const config = loadConfig(); const logger = new Logger(__filename); + if (!config.server.stripeConfig.isEnabled) { + logger.error("stripe is disabled but endpoint has been called"); + return res + .status(StatusCodes.NOT_FOUND) + .json({ message: "Endpoint not found" }); + } + const user = await getUserWithRoleFromRequest(req, res); if (!user) { diff --git a/pages/api/frontend/v0.1/stripe/products.ts b/pages/api/frontend/v0.1/stripe/products.ts index 963b5a4d3..5ff25f33c 100644 --- a/pages/api/frontend/v0.1/stripe/products.ts +++ b/pages/api/frontend/v0.1/stripe/products.ts @@ -153,6 +153,15 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse ) { + const stripeConfig = loadConfig().server.stripeConfig; + + if (!stripeConfig.isEnabled) { + logger.error("stripe is disabled but endpoint has been called"); + return res + .status(StatusCodes.NOT_FOUND) + .json({ message: "Endpoint not found" }); + } + switch (req.method) { case "GET": try { diff --git a/pages/api/frontend/v0.1/stripe/subscriptions/[orgId].ts b/pages/api/frontend/v0.1/stripe/subscriptions/[orgId].ts index 0465c5ece..2155629a1 100644 --- a/pages/api/frontend/v0.1/stripe/subscriptions/[orgId].ts +++ b/pages/api/frontend/v0.1/stripe/subscriptions/[orgId].ts @@ -1,5 +1,6 @@ import { StatusCodes } from "http-status-codes"; import type { NextApiRequest, NextApiResponse } from "next"; +import { loadConfig } from "../../../../../../config/loadConfig"; import prisma from "../../../../../../lib/services/db"; import { getUserWithRoleFromRequest } from "../../../../../../util/auth"; import { Logger } from "../../../../../../util/logger"; @@ -9,6 +10,14 @@ export default async function handler( res: NextApiResponse ) { const logger = new Logger(__filename); + const stripeConfig = loadConfig().server.stripeConfig; + + if (!stripeConfig.isEnabled) { + logger.error("stripe is disabled but endpoint has been called"); + return res + .status(StatusCodes.NOT_FOUND) + .json({ message: "Endpoint not found" }); + } const userInOrg = await getUserWithRoleFromRequest(req, res); diff --git a/pages/api/usageReport.ts b/pages/api/usageReport.ts index 39acfc101..113dd2fb8 100644 --- a/pages/api/usageReport.ts +++ b/pages/api/usageReport.ts @@ -8,8 +8,16 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse ) { - const usageReportingConfig = loadConfig().server.usageReport; const logger = new Logger(__filename); + const usageReportingConfig = loadConfig().server.usageReport; + const stripeConfig = loadConfig().server.stripeConfig; + + if (!stripeConfig.isEnabled) { + logger.error("stripe is disabled but endpoint has been called"); + return res + .status(StatusCodes.NOT_FOUND) + .json({ message: "Endpoint not found" }); + } if ( req.headers.authorization !== `Bearer ${usageReportingConfig.apiKey}` && diff --git a/pages/api/v0.1/messages.ts b/pages/api/v0.1/messages.ts index 58b5a9a4a..8721683e9 100644 --- a/pages/api/v0.1/messages.ts +++ b/pages/api/v0.1/messages.ts @@ -128,97 +128,99 @@ export default async function handler( } // Start of quota limitation - try { - const products = await getProducts(); + if (config.server.stripeConfig.isEnabled) { + try { + const products = await getProducts(); - // Check if there is a subItem with isMetered set to true - // Metered subItems do not have a limit - let hasMeteredSubItem = false; - // There should be 0 or 1 sub - let subFromDb = app?.organisation?.subs[0]; + // Check if there is a subItem with isMetered set to true + // Metered subItems do not have a limit + let hasMeteredSubItem = false; + // There should be 0 or 1 sub + let subFromDb = app?.organisation?.subs[0]; - if (app?.organisation?.subs) { - for (const sub of app.organisation.subs) { - if (sub.subItems?.some((subItem) => subItem.metered === true)) { - hasMeteredSubItem = true; - break; + if (app?.organisation?.subs) { + for (const sub of app.organisation.subs) { + if (sub.subItems?.some((subItem) => subItem.metered === true)) { + hasMeteredSubItem = true; + break; + } } } - } - // If not metered, check for the limit - if (!hasMeteredSubItem) { - let countingStartDate = new Date(); + // If not metered, check for the limit + if (!hasMeteredSubItem) { + let countingStartDate = new Date(); - // Free version counts back plainly one month - if (!subFromDb) { - countingStartDate.setMonth(countingStartDate.getMonth() - 1); - } else { - // use current period start of active subscription - countingStartDate = subFromDb.currentPeriodStart; - } + // Free version counts back plainly one month + if (!subFromDb) { + countingStartDate.setMonth(countingStartDate.getMonth() - 1); + } else { + // use current period start of active subscription + countingStartDate = subFromDb.currentPeriodStart; + } - // Prepare array of app ids of organisation - const appIds = app?.organisation?.apps?.map((app) => app.id) || []; + // Prepare array of app ids of organisation + const appIds = app?.organisation?.apps?.map((app) => app.id) || []; - // Count requests across all apps of the org - const requestCount = await prisma.loggedApiRequests.count({ - where: { - appId: { - in: appIds, - }, - createdAt: { - gte: countingStartDate, + // Count requests across all apps of the org + const requestCount = await prisma.loggedApiRequests.count({ + where: { + appId: { + in: appIds, + }, + createdAt: { + gte: countingStartDate, + }, }, - }, - }); - logger.log( - `Request count for org with id '${app.orgId}' is ${requestCount}` - ); - - let isLimitReached = false; - - // Check whether quota/limit for the request has been met (active subscription) - if (subFromDb) { - const targetProduct = products.find( - (product: { id: string | undefined }) => - product.id === subFromDb?.subItems[0].productId + }); + logger.log( + `Request count for org with id '${app.orgId}' is ${requestCount}` ); - if (!targetProduct) { - logger.error( - `No product found for org with id '${app.orgId}' and active sub with id '${subFromDb.subId}'` + let isLimitReached = false; + + // Check whether quota/limit for the request has been met (active subscription) + if (subFromDb) { + const targetProduct = products.find( + (product: { id: string | undefined }) => + product.id === subFromDb?.subItems[0].productId ); - return res - .status(StatusCodes.INTERNAL_SERVER_ERROR) - .json({ message: "Please try again later" }); - } - logger.log( - `Request limit for org with id '${app.orgId}' is ${targetProduct.requests}` - ); - if (requestCount >= Number(targetProduct.requests)) { + if (!targetProduct) { + logger.error( + `No product found for org with id '${app.orgId}' and active sub with id '${subFromDb.subId}'` + ); + return res + .status(StatusCodes.INTERNAL_SERVER_ERROR) + .json({ message: "Please try again later" }); + } + + logger.log( + `Request limit for org with id '${app.orgId}' is ${targetProduct.requests}` + ); + if (requestCount >= Number(targetProduct.requests)) { + isLimitReached = true; + } + } else if (!subFromDb && requestCount >= FREE_SUB_REQUEST_LIMIT) { + // Check quota/limit for free version isLimitReached = true; } - } else if (!subFromDb && requestCount >= FREE_SUB_REQUEST_LIMIT) { - // Check quota/limit for free version - isLimitReached = true; - } - // Return error if limit has been reached and the request cannot be served - if (isLimitReached) { - logger.log( - `The limit has been currently reached for org with id '${app?.orgId}'` - ); - return res.status(StatusCodes.PAYMENT_REQUIRED).json({ - message: "The limit for the current abo has been reached.", - }); + // Return error if limit has been reached and the request cannot be served + if (isLimitReached) { + logger.log( + `The limit has been currently reached for org with id '${app?.orgId}'` + ); + return res.status(StatusCodes.PAYMENT_REQUIRED).json({ + message: "The limit for the current abo has been reached.", + }); + } } + } catch (error: any) { + return res + .status(StatusCodes.INTERNAL_SERVER_ERROR) + .json({ message: error.message }); } - } catch (error: any) { - return res - .status(StatusCodes.INTERNAL_SERVER_ERROR) - .json({ message: error.message }); } logger.log(`Looking up all messages for app with id '${app.id}'`); diff --git a/pages/api/webhooks/stripe.ts b/pages/api/webhooks/stripe.ts index e3edf8aa4..f571685ef 100644 --- a/pages/api/webhooks/stripe.ts +++ b/pages/api/webhooks/stripe.ts @@ -31,6 +31,13 @@ export default async function handler( apiVersion: "2023-08-16", }); + if (!stripeConfig.isEnabled) { + logger.error("stripe is disabled but endpoint has been called"); + return res + .status(StatusCodes.NOT_FOUND) + .json({ message: "Endpoint not found" }); + } + switch (req.method) { case "POST": const buf = await buffer(req); diff --git a/pages/orgs/[orgId]/settings.tsx b/pages/orgs/[orgId]/settings.tsx index 6a014c4ea..8429aeebd 100644 --- a/pages/orgs/[orgId]/settings.tsx +++ b/pages/orgs/[orgId]/settings.tsx @@ -45,6 +45,7 @@ import { useUsers } from "../../../api/orgs/useUsers"; import createCustomerPortalSession from "../../../api/stripe/createCustomerPortalSession"; import getSubscriptions from "../../../api/stripe/getSubscriptions"; import resetOrgInvitationToken from "../../../api/tokens/resetOrgInvitationToken"; +import { loadConfig } from "../../../config/loadConfig"; import { Org } from "../../../models/org"; import { Subscription } from "../../../models/subscription"; import Routes from "../../../routes/routes"; @@ -54,6 +55,7 @@ import { getColorLabel, translateSubName } from "../../../util/nameTag"; export default function EditOrgPage() { const router = useRouter(); const toast = useToast(); + const config = loadConfig(); const { isOpen, onOpen, onClose } = useDisclosure(); const cancelRef = React.useRef(null); @@ -111,8 +113,12 @@ export default function EditOrgPage() { } setLoading(false); }; - - fetchUserSubscriptions(); + console.log("stripe enabled: " + config.server.stripeConfig.isEnabled); + if (config.server.stripeConfig.isEnabled) { + // TODO + console.log("if condition was true"); + fetchUserSubscriptions(); + } }, [router.isReady, orgId, toast]); const { data: session } = useSession(); @@ -545,7 +551,7 @@ export default function EditOrgPage() { {users?.length == 0 &&

no data to show

} - {userRole === "ADMIN" && ( + {config.server.stripeConfig.isEnabled && userRole === "ADMIN" && ( <> Your subscription From 1dcf3d0c2cecdac34a418cafacaad4730b31424a Mon Sep 17 00:00:00 2001 From: Florian Hochreiter Date: Sat, 11 Nov 2023 13:48:28 +0100 Subject: [PATCH 02/10] wip --- .env.example | 2 +- config/loadConfig.ts | 3 +- pages/orgs/[orgId]/settings.tsx | 202 ++++++++++++++++---------------- pages/orgs/[orgId]/upgrade.tsx | 10 ++ pages/subscription.tsx | 10 ++ 5 files changed, 127 insertions(+), 100 deletions(-) diff --git a/.env.example b/.env.example index f0a6b7b0d..caa84ac9d 100644 --- a/.env.example +++ b/.env.example @@ -19,7 +19,7 @@ SMTP_FROM_EMAIL_ADDRESS="flo@kula.app" SMTP_FROM_NAME="Flo Ho" # add your stripe API key here -STRIPE_ENABLED=false +NEXT_PUBLIC_STRIPE_ENABLED=false STRIPE_WEBHOOK_SECRET= STRIPE_SECRET_KEY= STRIPE_USE_AUTOMATIC_TAX= diff --git a/config/loadConfig.ts b/config/loadConfig.ts index 353c7f0b1..ec5a524a4 100644 --- a/config/loadConfig.ts +++ b/config/loadConfig.ts @@ -107,7 +107,8 @@ export function loadConfig(): Config { sentinelPassword: env.REDIS_SENTINEL_PASSWORD, }, stripeConfig: { - isEnabled: parseBooleanEnvValue(env.STRIPE_ENABLED) ?? false, + isEnabled: + parseBooleanEnvValue(env.NEXT_PUBLIC_STRIPE_ENABLED) ?? false, apiVersion: env.STRIPE_API_VERSION || "", webhookSecret: env.STRIPE_WEBHOOK_SECRET || "", secretKey: env.STRIPE_SECRET_KEY || "", diff --git a/pages/orgs/[orgId]/settings.tsx b/pages/orgs/[orgId]/settings.tsx index 8429aeebd..535dcd12e 100644 --- a/pages/orgs/[orgId]/settings.tsx +++ b/pages/orgs/[orgId]/settings.tsx @@ -46,6 +46,7 @@ import createCustomerPortalSession from "../../../api/stripe/createCustomerPorta import getSubscriptions from "../../../api/stripe/getSubscriptions"; import resetOrgInvitationToken from "../../../api/tokens/resetOrgInvitationToken"; import { loadConfig } from "../../../config/loadConfig"; +import { parseBooleanEnvValue } from "../../../config/parser/parseBooleanEnvValue"; import { Org } from "../../../models/org"; import { Subscription } from "../../../models/subscription"; import Routes from "../../../routes/routes"; @@ -113,10 +114,8 @@ export default function EditOrgPage() { } setLoading(false); }; - console.log("stripe enabled: " + config.server.stripeConfig.isEnabled); - if (config.server.stripeConfig.isEnabled) { - // TODO - console.log("if condition was true"); + + if (parseBooleanEnvValue(process.env.NEXT_PUBLIC_STRIPE_ENABLED)) { fetchUserSubscriptions(); } }, [router.isReady, orgId, toast]); @@ -551,105 +550,112 @@ export default function EditOrgPage() { {users?.length == 0 &&

no data to show

} - {config.server.stripeConfig.isEnabled && userRole === "ADMIN" && ( + {userRole === "ADMIN" && ( <> - - Your subscription - - {!loading && subs?.length != 0 && ( -
- - - - - - - - - - - {subs?.map((sub, index) => { - return ( - - - - - - ); - })} - -
- Org Id - - Organisation - - Subscription -
{sub.org.id}{sub.org.name} - - {translateSubName(sub.subName)} - -
- -
- -
- {loading && ( + {parseBooleanEnvValue( + process.env.NEXT_PUBLIC_STRIPE_ENABLED + ) && ( + <> + + Your subscription + + {!loading && subs?.length != 0 && (
- loading ... - + + + + + + + + + + + {subs?.map((sub, index) => { + return ( + + + + + + ); + })} + +
+ Org Id + + Organisation + + Subscription +
{sub.org.id}{sub.org.name} + + {translateSubName(sub.subName)} + +
+ +
+ +
+ {loading && ( +
+ + loading ... + + +
+ )}
)} -
- )} - {!loading && subs?.length == 0 && ( -
- currently no active subscription -
- )} - {!loading && subs?.length == 0 && ( -
- -
- )} - {!loading && subs?.length == 0 && isCustomer && ( -
- -
+ {!loading && subs?.length == 0 && ( +
+ currently no active subscription +
+ )} + {!loading && subs?.length == 0 && ( +
+ +
+ )} + {!loading && subs?.length == 0 && isCustomer && ( +
+ +
+ )} + )} -
Delete Organisation diff --git a/pages/orgs/[orgId]/upgrade.tsx b/pages/orgs/[orgId]/upgrade.tsx index 97ff18aeb..2f19e4087 100644 --- a/pages/orgs/[orgId]/upgrade.tsx +++ b/pages/orgs/[orgId]/upgrade.tsx @@ -5,6 +5,7 @@ import styles from "../../../styles/Home.module.css"; import { Heading, Skeleton, useToast } from "@chakra-ui/react"; import { useProducts } from "../../../api/stripe/useProducts"; import ProductCard from "../../../components/ProductCard"; +import { parseBooleanEnvValue } from "../../../config/parser/parseBooleanEnvValue"; export default function EditOrgPage() { const router = useRouter(); @@ -63,6 +64,15 @@ export async function getServerSideProps(context: any) { }; } + if (!parseBooleanEnvValue(process.env.NEXT_PUBLIC_STRIPE_ENABLED)) { + return { + redirect: { + destination: "/dashboard", + permanent: false, + }, + }; + } + return { props: { session }, }; diff --git a/pages/subscription.tsx b/pages/subscription.tsx index f1a31ce44..18dc5b960 100644 --- a/pages/subscription.tsx +++ b/pages/subscription.tsx @@ -2,6 +2,7 @@ import { Button, Heading, Spinner, Stack, useToast } from "@chakra-ui/react"; import { getSession } from "next-auth/react"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; +import { parseBooleanEnvValue } from "../config/parser/parseBooleanEnvValue"; import Routes from "../routes/routes"; import styles from "../styles/Home.module.css"; @@ -110,6 +111,15 @@ export async function getServerSideProps(context: any) { }; } + if (!parseBooleanEnvValue(process.env.NEXT_PUBLIC_STRIPE_ENABLED)) { + return { + redirect: { + destination: "/dashboard", + permanent: false, + }, + }; + } + return { props: { session }, }; From 4aab5f8a578102dd90eb1484adabb8b81b3421e9 Mon Sep 17 00:00:00 2001 From: Florian Hochreiter Date: Fri, 17 Nov 2023 19:39:36 +0100 Subject: [PATCH 03/10] wip --- config/interfaces/Config.ts | 1 + config/interfaces/StripeConfig.ts | 8 ++++---- config/loadConfig.ts | 6 ++++++ pages/api/frontend/v0.1/stripe/checkoutSession.ts | 4 ++-- pages/api/frontend/v0.1/stripe/customerPortal.ts | 3 ++- pages/api/frontend/v0.1/stripe/products.ts | 5 ++++- pages/api/webhooks/stripe.ts | 2 +- util/stripe/reportUsage.ts | 4 +++- 8 files changed, 23 insertions(+), 10 deletions(-) diff --git a/config/interfaces/Config.ts b/config/interfaces/Config.ts index d8ffaf0f7..ff73faa67 100644 --- a/config/interfaces/Config.ts +++ b/config/interfaces/Config.ts @@ -13,6 +13,7 @@ import { UsageReportConfig } from "./UsageReportConfig"; export interface Config { client: { sentryConfig: SentryConfig; + stripeConfig: StripeConfig; }; server: { database: DatabaseConfig; diff --git a/config/interfaces/StripeConfig.ts b/config/interfaces/StripeConfig.ts index 583ad0799..44038c5f4 100644 --- a/config/interfaces/StripeConfig.ts +++ b/config/interfaces/StripeConfig.ts @@ -1,9 +1,9 @@ export interface StripeConfig { isEnabled: boolean; - apiVersion: string; - webhookSecret: string; - secretKey: string; - useAutomaticTax: boolean; + apiVersion?: string; + webhookSecret?: string; + secretKey?: string; + useAutomaticTax?: boolean; dynamicTaxRates?: Array; taxRates?: Array; } diff --git a/config/loadConfig.ts b/config/loadConfig.ts index ec5a524a4..c1ef38355 100644 --- a/config/loadConfig.ts +++ b/config/loadConfig.ts @@ -64,6 +64,12 @@ export function loadConfig(): Config { env.NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE ) ?? 0.2, }, + stripeConfig: { + isEnabled: + parseBooleanEnvValue( + env.STRIPE_ENABLED ?? env.NEXT_PUBLIC_STRIPE_ENABLED + ) ?? false, + }, }, server: { nextAuth: { diff --git a/pages/api/frontend/v0.1/stripe/checkoutSession.ts b/pages/api/frontend/v0.1/stripe/checkoutSession.ts index ce1ff886c..ea7b42e53 100644 --- a/pages/api/frontend/v0.1/stripe/checkoutSession.ts +++ b/pages/api/frontend/v0.1/stripe/checkoutSession.ts @@ -37,7 +37,7 @@ export default async function handler( switch (req.method) { case "POST": - const stripe = new Stripe(stripeConfig.secretKey, { + const stripe = new Stripe(stripeConfig.secretKey as string, { apiVersion: "2023-08-16", }); @@ -101,7 +101,7 @@ export default async function handler( let sessionOptions: Stripe.Checkout.SessionCreateParams = { allow_promotion_codes: true, automatic_tax: { - enabled: stripeConfig.useAutomaticTax, + enabled: !!stripeConfig.useAutomaticTax, }, billing_address_collection: "required", client_reference_id: req.body.orgId, diff --git a/pages/api/frontend/v0.1/stripe/customerPortal.ts b/pages/api/frontend/v0.1/stripe/customerPortal.ts index b2de7fc66..301480105 100644 --- a/pages/api/frontend/v0.1/stripe/customerPortal.ts +++ b/pages/api/frontend/v0.1/stripe/customerPortal.ts @@ -38,7 +38,8 @@ export default async function handler( switch (req.method) { case "POST": - const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", { + const stripeConfig = loadConfig().server.stripeConfig; + const stripe = new Stripe(stripeConfig.secretKey as string, { apiVersion: "2023-08-16", }); diff --git a/pages/api/frontend/v0.1/stripe/products.ts b/pages/api/frontend/v0.1/stripe/products.ts index 5ff25f33c..5a584e469 100644 --- a/pages/api/frontend/v0.1/stripe/products.ts +++ b/pages/api/frontend/v0.1/stripe/products.ts @@ -11,9 +11,12 @@ const logger = new Logger(__filename); export async function getProducts(): Promise { const config = loadConfig(); - const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", { + + const stripeConfig = loadConfig().server.stripeConfig; + const stripe = new Stripe(stripeConfig.secretKey as string, { apiVersion: "2023-08-16", }); + const freeProduct: Product = { id: "FREE", description: "For checking it out", diff --git a/pages/api/webhooks/stripe.ts b/pages/api/webhooks/stripe.ts index f571685ef..c4f6d5158 100644 --- a/pages/api/webhooks/stripe.ts +++ b/pages/api/webhooks/stripe.ts @@ -27,7 +27,7 @@ export default async function handler( const logger = new Logger(__filename); const stripeConfig = loadConfig().server.stripeConfig; - const stripe = new Stripe(stripeConfig.secretKey, { + const stripe = new Stripe(stripeConfig.secretKey as string, { apiVersion: "2023-08-16", }); diff --git a/util/stripe/reportUsage.ts b/util/stripe/reportUsage.ts index 94463a6f2..bc2de343d 100644 --- a/util/stripe/reportUsage.ts +++ b/util/stripe/reportUsage.ts @@ -1,10 +1,12 @@ import Stripe from "stripe"; +import { loadConfig } from "../../config/loadConfig"; import prisma from "../../lib/services/db"; import { Product } from "../../models/product"; import { getProducts } from "../../pages/api/frontend/v0.1/stripe/products"; import { Logger } from "../logger"; -const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", { +const stripeConfig = loadConfig().server.stripeConfig; +const stripe = new Stripe(stripeConfig.secretKey as string, { apiVersion: "2023-08-16", }); From 66e67e952a22305045655f258bed7769e9d75024 Mon Sep 17 00:00:00 2001 From: Florian Hochreiter Date: Wed, 6 Dec 2023 15:15:24 +0100 Subject: [PATCH 04/10] wip --- pages/dashboard.tsx | 77 +++++++++++++++++---------------- pages/orgs/[orgId]/settings.tsx | 2 +- pages/orgs/[orgId]/upgrade.tsx | 26 ++++++----- pages/orgs/new.tsx | 69 ++++++++++++++++------------- types/globals.d.ts | 5 +++ 5 files changed, 100 insertions(+), 79 deletions(-) create mode 100644 types/globals.d.ts diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx index 4e59a2677..cc1c9331d 100644 --- a/pages/dashboard.tsx +++ b/pages/dashboard.tsx @@ -1,41 +1,38 @@ -import { useRouter } from "next/router"; -import { useEffect, useState } from "react"; -import styles from "../styles/Home.module.css"; -import { getSession } from "next-auth/react"; -import getDirectInviteToken from "../api/tokens/getDirectInviteToken"; -import getOrgInviteToken from "../api/tokens/getOrgInviteToken"; -import joinOrgViaDirectInvite from "../api/tokens/joinOrgViaDirectInvite"; -import joinOrgViaOrgInvite from "../api/tokens/joinOrgViaOrgInvite"; -import { useOrgs } from "../api/orgs/useOrgs"; -import Routes from "../routes/routes"; -import { OrgInvite } from "../models/orgInvite"; import { - Button, - Table, - Thead, - Th, - Tr, - Td, - Tbody, AlertDialog, AlertDialogBody, + AlertDialogCloseButton, + AlertDialogContent, AlertDialogFooter, AlertDialogHeader, - AlertDialogContent, AlertDialogOverlay, - AlertDialogCloseButton, - useToast, - useDisclosure, + Button, + Heading, Skeleton, Stack, - Heading, + Table, Tag, + Tbody, + Td, + Th, + Thead, + Tr, + useDisclosure, + useToast, } from "@chakra-ui/react"; -import React from "react"; -import { - getColorLabel, - translateSubName, -} from "../util/nameTag"; +import { getSession } from "next-auth/react"; +import { useRouter } from "next/router"; +import React, { useEffect, useState } from "react"; +import { useOrgs } from "../api/orgs/useOrgs"; +import getDirectInviteToken from "../api/tokens/getDirectInviteToken"; +import getOrgInviteToken from "../api/tokens/getOrgInviteToken"; +import joinOrgViaDirectInvite from "../api/tokens/joinOrgViaDirectInvite"; +import joinOrgViaOrgInvite from "../api/tokens/joinOrgViaOrgInvite"; +import { parseBooleanEnvValue } from "../config/parser/parseBooleanEnvValue"; +import { OrgInvite } from "../models/orgInvite"; +import Routes from "../routes/routes"; +import styles from "../styles/Home.module.css"; +import { getColorLabel, translateSubName } from "../util/nameTag"; export default function DashboardPage() { const router = useRouter(); @@ -159,16 +156,20 @@ export default function DashboardPage() { {org.id} {org.name} - - {translateSubName(org?.subName)} - + {parseBooleanEnvValue( + window.__env.NEXT_PUBLIC_STRIPE_ENABLED + ) && ( + + {translateSubName(org?.subName)} + + )} ); diff --git a/pages/orgs/[orgId]/settings.tsx b/pages/orgs/[orgId]/settings.tsx index 535dcd12e..eca113f3d 100644 --- a/pages/orgs/[orgId]/settings.tsx +++ b/pages/orgs/[orgId]/settings.tsx @@ -553,7 +553,7 @@ export default function EditOrgPage() { {userRole === "ADMIN" && ( <> {parseBooleanEnvValue( - process.env.NEXT_PUBLIC_STRIPE_ENABLED + window.__env.NEXT_PUBLIC_STRIPE_ENABLED ) && ( <> diff --git a/pages/orgs/[orgId]/upgrade.tsx b/pages/orgs/[orgId]/upgrade.tsx index 2f19e4087..5c5be7559 100644 --- a/pages/orgs/[orgId]/upgrade.tsx +++ b/pages/orgs/[orgId]/upgrade.tsx @@ -3,17 +3,32 @@ import { useRouter } from "next/router"; import styles from "../../../styles/Home.module.css"; import { Heading, Skeleton, useToast } from "@chakra-ui/react"; +import { useEffect } from "react"; import { useProducts } from "../../../api/stripe/useProducts"; import ProductCard from "../../../components/ProductCard"; import { parseBooleanEnvValue } from "../../../config/parser/parseBooleanEnvValue"; +import Routes from "../../../routes/routes"; export default function EditOrgPage() { const router = useRouter(); const toast = useToast(); - const { products, isError, isLoading } = useProducts(); const orgId = Number(router.query.orgId); + useEffect(() => { + if (!router.isReady) return; + + function navigateToDashboardPage() { + router.push(Routes.DASHBOARD); + } + + if (!parseBooleanEnvValue(window.__env.NEXT_PUBLIC_STRIPE_ENABLED)) { + navigateToDashboardPage(); + } + }, [router.isReady, router]); + + const { products, isError, isLoading } = useProducts(); + if (isError) { toast({ title: "Error!", @@ -64,15 +79,6 @@ export async function getServerSideProps(context: any) { }; } - if (!parseBooleanEnvValue(process.env.NEXT_PUBLIC_STRIPE_ENABLED)) { - return { - redirect: { - destination: "/dashboard", - permanent: false, - }, - }; - } - return { props: { session }, }; diff --git a/pages/orgs/new.tsx b/pages/orgs/new.tsx index 3fbb3eb38..544393a58 100644 --- a/pages/orgs/new.tsx +++ b/pages/orgs/new.tsx @@ -1,17 +1,18 @@ -import { FormEvent, useState } from "react"; -import styles from "../../styles/Home.module.css"; -import { getSession } from "next-auth/react"; import { - Input, - useToast, - Heading, + Button, FormControl, FormLabel, - Button, + Heading, + Input, + useToast, } from "@chakra-ui/react"; +import { getSession } from "next-auth/react"; +import { useRouter } from "next/router"; +import { FormEvent, useState } from "react"; import createOrg from "../../api/orgs/createOrg"; +import { parseBooleanEnvValue } from "../../config/parser/parseBooleanEnvValue"; import Routes from "../../routes/routes"; -import { useRouter } from "next/router"; +import styles from "../../styles/Home.module.css"; export default function NewOrgPage() { const router = useRouter(); @@ -24,10 +25,10 @@ export default function NewOrgPage() { try { const org = await createOrg(orgName); - + toast({ title: "Success!", - description: 'New organisation created.', + description: "New organisation created.", status: "success", isClosable: true, duration: 6000, @@ -35,7 +36,11 @@ export default function NewOrgPage() { resetForm(); - navigateToUpgradePage(org.orgId); + if (parseBooleanEnvValue(window.__env.NEXT_PUBLIC_STRIPE_ENABLED)) { + navigateToUpgradePage(org.orgId); + } else { + navigateToOrgAppsPage(org.orgId); + } } catch (error) { toast({ title: "Error while creating new organisation!", @@ -46,11 +51,15 @@ export default function NewOrgPage() { }); } } - + function navigateToUpgradePage(orgId: number) { router.push(Routes.getOrgUpgradeByOrgId(orgId)); } + function navigateToOrgAppsPage(orgId: number) { + router.push(Routes.getOrgAppsByOrgId(orgId)); + } + function resetForm() { (document.getElementById("orgForm") as HTMLFormElement)?.reset(); } @@ -59,24 +68,24 @@ export default function NewOrgPage() { <>
-
- New Organisation -
- -
- Name - setOrgName(event.target.value)} - /> -
- -
-
-
+
+ New Organisation +
+ +
+ Name + setOrgName(event.target.value)} + /> +
+ +
+
+
diff --git a/types/globals.d.ts b/types/globals.d.ts new file mode 100644 index 000000000..585dddd32 --- /dev/null +++ b/types/globals.d.ts @@ -0,0 +1,5 @@ +interface Window { + __env: { + [key: string]: string; + }; +} From 26f9efee5b17be087aa4fca66344debb42d01a97 Mon Sep 17 00:00:00 2001 From: Florian Hochreiter Date: Wed, 6 Dec 2023 15:21:01 +0100 Subject: [PATCH 05/10] wip --- config/loadConfig.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/config/loadConfig.ts b/config/loadConfig.ts index c1ef38355..850a12c75 100644 --- a/config/loadConfig.ts +++ b/config/loadConfig.ts @@ -66,9 +66,7 @@ export function loadConfig(): Config { }, stripeConfig: { isEnabled: - parseBooleanEnvValue( - env.STRIPE_ENABLED ?? env.NEXT_PUBLIC_STRIPE_ENABLED - ) ?? false, + parseBooleanEnvValue(env.NEXT_PUBLIC_STRIPE_ENABLED) ?? false, }, }, server: { From 4b35c0a6b731c0baa7b1facf60f84eb4eadb86ff Mon Sep 17 00:00:00 2001 From: Florian Hochreiter Date: Wed, 6 Dec 2023 15:28:55 +0100 Subject: [PATCH 06/10] wip --- pages/orgs/[orgId]/settings.tsx | 2 +- pages/subscription.tsx | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/pages/orgs/[orgId]/settings.tsx b/pages/orgs/[orgId]/settings.tsx index eca113f3d..9bbc95cec 100644 --- a/pages/orgs/[orgId]/settings.tsx +++ b/pages/orgs/[orgId]/settings.tsx @@ -115,7 +115,7 @@ export default function EditOrgPage() { setLoading(false); }; - if (parseBooleanEnvValue(process.env.NEXT_PUBLIC_STRIPE_ENABLED)) { + if (parseBooleanEnvValue(window.__env.NEXT_PUBLIC_STRIPE_ENABLED)) { fetchUserSubscriptions(); } }, [router.isReady, orgId, toast]); diff --git a/pages/subscription.tsx b/pages/subscription.tsx index 18dc5b960..43fb5b03d 100644 --- a/pages/subscription.tsx +++ b/pages/subscription.tsx @@ -14,6 +14,18 @@ export default function ProfilePage() { let { success, canceled, orgId } = router.query; + useEffect(() => { + if (!router.isReady) return; + + function navigateToDashboardPage() { + router.push(Routes.DASHBOARD); + } + + if (!parseBooleanEnvValue(window.__env.NEXT_PUBLIC_STRIPE_ENABLED)) { + navigateToDashboardPage(); + } + }, [router.isReady, router]); + function navigateToDashboardPage() { router.push(Routes.DASHBOARD); } @@ -111,15 +123,6 @@ export async function getServerSideProps(context: any) { }; } - if (!parseBooleanEnvValue(process.env.NEXT_PUBLIC_STRIPE_ENABLED)) { - return { - redirect: { - destination: "/dashboard", - permanent: false, - }, - }; - } - return { props: { session }, }; From bbe4e030c0d66dc2a68e70fe3689f7fccd65dd74 Mon Sep 17 00:00:00 2001 From: Florian Hochreiter <38766452+flohoch@users.noreply.github.com> Date: Sun, 10 Dec 2023 12:10:14 +0100 Subject: [PATCH 07/10] Update config/loadConfig.ts Co-authored-by: Philip Niedertscheider --- config/loadConfig.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/loadConfig.ts b/config/loadConfig.ts index 850a12c75..648ef06f8 100644 --- a/config/loadConfig.ts +++ b/config/loadConfig.ts @@ -111,8 +111,7 @@ export function loadConfig(): Config { sentinelPassword: env.REDIS_SENTINEL_PASSWORD, }, stripeConfig: { - isEnabled: - parseBooleanEnvValue(env.NEXT_PUBLIC_STRIPE_ENABLED) ?? false, + isEnabled: parseBooleanEnvValue(env.STRIPE_ENABLED) ?? false, apiVersion: env.STRIPE_API_VERSION || "", webhookSecret: env.STRIPE_WEBHOOK_SECRET || "", secretKey: env.STRIPE_SECRET_KEY || "", From d14f108547ec71d3dcf36c5f359e170d8518d7ed Mon Sep 17 00:00:00 2001 From: Florian Hochreiter <38766452+flohoch@users.noreply.github.com> Date: Sun, 10 Dec 2023 12:14:38 +0100 Subject: [PATCH 08/10] Update config/loadConfig.ts Co-authored-by: Philip Niedertscheider --- config/loadConfig.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config/loadConfig.ts b/config/loadConfig.ts index 648ef06f8..a17527485 100644 --- a/config/loadConfig.ts +++ b/config/loadConfig.ts @@ -66,7 +66,9 @@ export function loadConfig(): Config { }, stripeConfig: { isEnabled: - parseBooleanEnvValue(env.NEXT_PUBLIC_STRIPE_ENABLED) ?? false, + parseBooleanEnvValue(env.STRIPE_ENABLED) ?? + parseBooleanEnvValue(env.NEXT_PUBLIC_STRIPE_ENABLED) ?? + false, }, }, server: { From d99a29098e125bb06db910e1556e35c155518347 Mon Sep 17 00:00:00 2001 From: Florian Hochreiter <38766452+flohoch@users.noreply.github.com> Date: Sun, 10 Dec 2023 12:15:20 +0100 Subject: [PATCH 09/10] Update pages/api/frontend/v0.1/stripe/checkoutSession.ts Co-authored-by: Philip Niedertscheider --- pages/api/frontend/v0.1/stripe/checkoutSession.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pages/api/frontend/v0.1/stripe/checkoutSession.ts b/pages/api/frontend/v0.1/stripe/checkoutSession.ts index ea7b42e53..2e8113e4d 100644 --- a/pages/api/frontend/v0.1/stripe/checkoutSession.ts +++ b/pages/api/frontend/v0.1/stripe/checkoutSession.ts @@ -37,7 +37,10 @@ export default async function handler( switch (req.method) { case "POST": - const stripe = new Stripe(stripeConfig.secretKey as string, { + if (!stripeConfig.secretKey) { + throw new Error("Stripe secret key is not configured"); + } + const stripe = new Stripe(stripeConfig.secretKey, { apiVersion: "2023-08-16", }); From 34f4bd2d28f6f36b74aed4568a84cb6678b6c3f9 Mon Sep 17 00:00:00 2001 From: Florian Hochreiter Date: Sun, 10 Dec 2023 12:46:54 +0100 Subject: [PATCH 10/10] updated according to pull request feedback --- .env.example | 1 + pages/api/frontend/v0.1/stripe/checkoutSession.ts | 4 ++-- pages/api/frontend/v0.1/stripe/customerPortal.ts | 11 +++++++---- pages/api/frontend/v0.1/stripe/products.ts | 9 ++++++--- .../api/frontend/v0.1/stripe/subscriptions/[orgId].ts | 4 ++-- pages/api/usageReport.ts | 4 ++-- pages/api/webhooks/stripe.ts | 9 ++++++--- pages/dashboard.tsx | 7 +++---- pages/orgs/[orgId]/settings.tsx | 11 ++++------- pages/orgs/[orgId]/upgrade.tsx | 7 ++++--- pages/orgs/new.tsx | 5 +++-- pages/subscription.tsx | 7 ++++--- types/globals.d.ts | 5 ----- util/stripe/reportUsage.ts | 5 ++++- 14 files changed, 48 insertions(+), 41 deletions(-) delete mode 100644 types/globals.d.ts diff --git a/.env.example b/.env.example index caa84ac9d..d415e8039 100644 --- a/.env.example +++ b/.env.example @@ -20,6 +20,7 @@ SMTP_FROM_NAME="Flo Ho" # add your stripe API key here NEXT_PUBLIC_STRIPE_ENABLED=false +STRIPE_ENABLED=false STRIPE_WEBHOOK_SECRET= STRIPE_SECRET_KEY= STRIPE_USE_AUTOMATIC_TAX= diff --git a/pages/api/frontend/v0.1/stripe/checkoutSession.ts b/pages/api/frontend/v0.1/stripe/checkoutSession.ts index 2e8113e4d..d41c536c5 100644 --- a/pages/api/frontend/v0.1/stripe/checkoutSession.ts +++ b/pages/api/frontend/v0.1/stripe/checkoutSession.ts @@ -18,8 +18,8 @@ export default async function handler( if (!stripeConfig.isEnabled) { logger.error("stripe is disabled but endpoint has been called"); return res - .status(StatusCodes.NOT_FOUND) - .json({ message: "Endpoint not found" }); + .status(StatusCodes.SERVICE_UNAVAILABLE) + .json({ message: "Endpoint is disabled" }); } const userInOrg = await getUserWithRoleFromRequest(req, res); diff --git a/pages/api/frontend/v0.1/stripe/customerPortal.ts b/pages/api/frontend/v0.1/stripe/customerPortal.ts index 301480105..c68837f1c 100644 --- a/pages/api/frontend/v0.1/stripe/customerPortal.ts +++ b/pages/api/frontend/v0.1/stripe/customerPortal.ts @@ -17,8 +17,8 @@ export default async function handler( if (!config.server.stripeConfig.isEnabled) { logger.error("stripe is disabled but endpoint has been called"); return res - .status(StatusCodes.NOT_FOUND) - .json({ message: "Endpoint not found" }); + .status(StatusCodes.SERVICE_UNAVAILABLE) + .json({ message: "Endpoint is disabled" }); } const user = await getUserWithRoleFromRequest(req, res); @@ -38,8 +38,11 @@ export default async function handler( switch (req.method) { case "POST": - const stripeConfig = loadConfig().server.stripeConfig; - const stripe = new Stripe(stripeConfig.secretKey as string, { + const stripeConfig = config.server.stripeConfig; + if (!stripeConfig.secretKey) { + throw new Error("Stripe secret key is not configured"); + } + const stripe = new Stripe(stripeConfig.secretKey, { apiVersion: "2023-08-16", }); diff --git a/pages/api/frontend/v0.1/stripe/products.ts b/pages/api/frontend/v0.1/stripe/products.ts index 88390623a..acfde6da9 100644 --- a/pages/api/frontend/v0.1/stripe/products.ts +++ b/pages/api/frontend/v0.1/stripe/products.ts @@ -13,7 +13,10 @@ export async function getProducts(): Promise { const config = loadConfig(); const stripeConfig = loadConfig().server.stripeConfig; - const stripe = new Stripe(stripeConfig.secretKey as string, { + if (!stripeConfig.secretKey) { + throw new Error("Stripe secret key is not configured"); + } + const stripe = new Stripe(stripeConfig.secretKey, { apiVersion: "2023-08-16", }); @@ -158,8 +161,8 @@ export default async function handler( if (!stripeConfig.isEnabled) { logger.error("stripe is disabled but endpoint has been called"); return res - .status(StatusCodes.NOT_FOUND) - .json({ message: "Endpoint not found" }); + .status(StatusCodes.SERVICE_UNAVAILABLE) + .json({ message: "Endpoint is disabled" }); } switch (req.method) { diff --git a/pages/api/frontend/v0.1/stripe/subscriptions/[orgId].ts b/pages/api/frontend/v0.1/stripe/subscriptions/[orgId].ts index 2155629a1..c95df635f 100644 --- a/pages/api/frontend/v0.1/stripe/subscriptions/[orgId].ts +++ b/pages/api/frontend/v0.1/stripe/subscriptions/[orgId].ts @@ -15,8 +15,8 @@ export default async function handler( if (!stripeConfig.isEnabled) { logger.error("stripe is disabled but endpoint has been called"); return res - .status(StatusCodes.NOT_FOUND) - .json({ message: "Endpoint not found" }); + .status(StatusCodes.SERVICE_UNAVAILABLE) + .json({ message: "Endpoint is disabled" }); } const userInOrg = await getUserWithRoleFromRequest(req, res); diff --git a/pages/api/usageReport.ts b/pages/api/usageReport.ts index 113dd2fb8..fc2ce99fc 100644 --- a/pages/api/usageReport.ts +++ b/pages/api/usageReport.ts @@ -15,8 +15,8 @@ export default async function handler( if (!stripeConfig.isEnabled) { logger.error("stripe is disabled but endpoint has been called"); return res - .status(StatusCodes.NOT_FOUND) - .json({ message: "Endpoint not found" }); + .status(StatusCodes.SERVICE_UNAVAILABLE) + .json({ message: "Endpoint is disabled" }); } if ( diff --git a/pages/api/webhooks/stripe.ts b/pages/api/webhooks/stripe.ts index c4f6d5158..fb2d8dda7 100644 --- a/pages/api/webhooks/stripe.ts +++ b/pages/api/webhooks/stripe.ts @@ -27,15 +27,18 @@ export default async function handler( const logger = new Logger(__filename); const stripeConfig = loadConfig().server.stripeConfig; - const stripe = new Stripe(stripeConfig.secretKey as string, { + if (!stripeConfig.secretKey) { + throw new Error("Stripe secret key is not configured"); + } + const stripe = new Stripe(stripeConfig.secretKey, { apiVersion: "2023-08-16", }); if (!stripeConfig.isEnabled) { logger.error("stripe is disabled but endpoint has been called"); return res - .status(StatusCodes.NOT_FOUND) - .json({ message: "Endpoint not found" }); + .status(StatusCodes.SERVICE_UNAVAILABLE) + .json({ message: "Endpoint is disabled" }); } switch (req.method) { diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx index cc1c9331d..1f3b3d409 100644 --- a/pages/dashboard.tsx +++ b/pages/dashboard.tsx @@ -28,7 +28,7 @@ import getDirectInviteToken from "../api/tokens/getDirectInviteToken"; import getOrgInviteToken from "../api/tokens/getOrgInviteToken"; import joinOrgViaDirectInvite from "../api/tokens/joinOrgViaDirectInvite"; import joinOrgViaOrgInvite from "../api/tokens/joinOrgViaOrgInvite"; -import { parseBooleanEnvValue } from "../config/parser/parseBooleanEnvValue"; +import { loadConfig } from "../config/loadConfig"; import { OrgInvite } from "../models/orgInvite"; import Routes from "../routes/routes"; import styles from "../styles/Home.module.css"; @@ -36,6 +36,7 @@ import { getColorLabel, translateSubName } from "../util/nameTag"; export default function DashboardPage() { const router = useRouter(); + const stripeConfig = loadConfig().client.stripeConfig; const { invite, directinvite } = router.query; @@ -156,9 +157,7 @@ export default function DashboardPage() { {org.id} {org.name} - {parseBooleanEnvValue( - window.__env.NEXT_PUBLIC_STRIPE_ENABLED - ) && ( + {stripeConfig.isEnabled && ( no data to show

} {userRole === "ADMIN" && ( <> - {parseBooleanEnvValue( - window.__env.NEXT_PUBLIC_STRIPE_ENABLED - ) && ( + {stripeConfig.isEnabled && ( <> Your subscription diff --git a/pages/orgs/[orgId]/upgrade.tsx b/pages/orgs/[orgId]/upgrade.tsx index 5c5be7559..9ec93f86c 100644 --- a/pages/orgs/[orgId]/upgrade.tsx +++ b/pages/orgs/[orgId]/upgrade.tsx @@ -6,12 +6,13 @@ import { Heading, Skeleton, useToast } from "@chakra-ui/react"; import { useEffect } from "react"; import { useProducts } from "../../../api/stripe/useProducts"; import ProductCard from "../../../components/ProductCard"; -import { parseBooleanEnvValue } from "../../../config/parser/parseBooleanEnvValue"; +import { loadConfig } from "../../../config/loadConfig"; import Routes from "../../../routes/routes"; export default function EditOrgPage() { const router = useRouter(); const toast = useToast(); + const stripeConfig = loadConfig().client.stripeConfig; const orgId = Number(router.query.orgId); @@ -22,10 +23,10 @@ export default function EditOrgPage() { router.push(Routes.DASHBOARD); } - if (!parseBooleanEnvValue(window.__env.NEXT_PUBLIC_STRIPE_ENABLED)) { + if (!stripeConfig.isEnabled) { navigateToDashboardPage(); } - }, [router.isReady, router]); + }, [router.isReady, router, stripeConfig.isEnabled]); const { products, isError, isLoading } = useProducts(); diff --git a/pages/orgs/new.tsx b/pages/orgs/new.tsx index 544393a58..9ea2ced8a 100644 --- a/pages/orgs/new.tsx +++ b/pages/orgs/new.tsx @@ -10,13 +10,14 @@ import { getSession } from "next-auth/react"; import { useRouter } from "next/router"; import { FormEvent, useState } from "react"; import createOrg from "../../api/orgs/createOrg"; -import { parseBooleanEnvValue } from "../../config/parser/parseBooleanEnvValue"; +import { loadConfig } from "../../config/loadConfig"; import Routes from "../../routes/routes"; import styles from "../../styles/Home.module.css"; export default function NewOrgPage() { const router = useRouter(); const toast = useToast(); + const stripeConfig = loadConfig().client.stripeConfig; const [orgName, setOrgName] = useState(""); @@ -36,7 +37,7 @@ export default function NewOrgPage() { resetForm(); - if (parseBooleanEnvValue(window.__env.NEXT_PUBLIC_STRIPE_ENABLED)) { + if (stripeConfig.isEnabled) { navigateToUpgradePage(org.orgId); } else { navigateToOrgAppsPage(org.orgId); diff --git a/pages/subscription.tsx b/pages/subscription.tsx index 43fb5b03d..a0e198dd6 100644 --- a/pages/subscription.tsx +++ b/pages/subscription.tsx @@ -2,13 +2,14 @@ import { Button, Heading, Spinner, Stack, useToast } from "@chakra-ui/react"; import { getSession } from "next-auth/react"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; -import { parseBooleanEnvValue } from "../config/parser/parseBooleanEnvValue"; +import { loadConfig } from "../config/loadConfig"; import Routes from "../routes/routes"; import styles from "../styles/Home.module.css"; export default function ProfilePage() { const router = useRouter(); const toast = useToast(); + const stripeConfig = loadConfig().client.stripeConfig; const [loading, setLoading] = useState(true); @@ -21,10 +22,10 @@ export default function ProfilePage() { router.push(Routes.DASHBOARD); } - if (!parseBooleanEnvValue(window.__env.NEXT_PUBLIC_STRIPE_ENABLED)) { + if (!stripeConfig.isEnabled) { navigateToDashboardPage(); } - }, [router.isReady, router]); + }, [router.isReady, router, stripeConfig.isEnabled]); function navigateToDashboardPage() { router.push(Routes.DASHBOARD); diff --git a/types/globals.d.ts b/types/globals.d.ts deleted file mode 100644 index 585dddd32..000000000 --- a/types/globals.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -interface Window { - __env: { - [key: string]: string; - }; -} diff --git a/util/stripe/reportUsage.ts b/util/stripe/reportUsage.ts index bc2de343d..7dac3ba08 100644 --- a/util/stripe/reportUsage.ts +++ b/util/stripe/reportUsage.ts @@ -6,7 +6,10 @@ import { getProducts } from "../../pages/api/frontend/v0.1/stripe/products"; import { Logger } from "../logger"; const stripeConfig = loadConfig().server.stripeConfig; -const stripe = new Stripe(stripeConfig.secretKey as string, { +if (!stripeConfig.secretKey) { + throw new Error("Stripe secret key is not configured"); +} +const stripe = new Stripe(stripeConfig.secretKey, { apiVersion: "2023-08-16", });