diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-alerts/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-alerts/route.tsx index 4dd0352f89..a67a9b02ea 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-alerts/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-alerts/route.tsx @@ -47,7 +47,7 @@ export const meta: MetaFunction = () => { }; export async function loader({ params, request }: LoaderFunctionArgs) { - await requireUserId(request); + const userId = await requireUserId(request); const { organizationSlug } = OrganizationParamsSchema.parse(params); const { isManagedCloud } = featuresForRequest(request); @@ -55,8 +55,8 @@ export async function loader({ params, request }: LoaderFunctionArgs) { return redirect(organizationPath({ slug: organizationSlug })); } - const organization = await prisma.organization.findUnique({ - where: { slug: organizationSlug }, + const organization = await prisma.organization.findFirst({ + where: { slug: organizationSlug, members: { some: { userId } } }, }); if (!organization) { diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing/route.tsx index 0dbffffc4d..e78fe138fb 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing/route.tsx @@ -28,7 +28,7 @@ export const meta: MetaFunction = () => { }; export async function loader({ params, request }: LoaderFunctionArgs) { - await requireUserId(request); + const userId = await requireUserId(request); const { organizationSlug } = OrganizationParamsSchema.parse(params); const { isManagedCloud } = featuresForRequest(request); @@ -41,8 +41,8 @@ export async function loader({ params, request }: LoaderFunctionArgs) { throw new Response(null, { status: 404, statusText: "Plans not found" }); } - const organization = await prisma.organization.findUnique({ - where: { slug: organizationSlug }, + const organization = await prisma.organization.findFirst({ + where: { slug: organizationSlug, members: { some: { userId } } }, }); if (!organization) { diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.usage/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.usage/route.tsx index 9e92c27f2b..cfefb3938c 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.usage/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.usage/route.tsx @@ -46,7 +46,7 @@ export const meta: MetaFunction = () => { }; export async function loader({ params, request }: LoaderFunctionArgs) { - await requireUserId(request); + const userId = await requireUserId(request); const { organizationSlug } = OrganizationParamsSchema.parse(params); const { isManagedCloud } = featuresForRequest(request); @@ -54,8 +54,8 @@ export async function loader({ params, request }: LoaderFunctionArgs) { return redirect(organizationPath({ slug: organizationSlug })); } - const organization = await prisma.organization.findUnique({ - where: { slug: organizationSlug }, + const organization = await prisma.organization.findFirst({ + where: { slug: organizationSlug, members: { some: { userId } } }, }); if (!organization) { diff --git a/apps/webapp/app/routes/account.tokens/route.tsx b/apps/webapp/app/routes/account.tokens/route.tsx index 7b7945c70f..4ad62d7edc 100644 --- a/apps/webapp/app/routes/account.tokens/route.tsx +++ b/apps/webapp/app/routes/account.tokens/route.tsx @@ -3,8 +3,8 @@ import { parse } from "@conform-to/zod"; import { BookOpenIcon, ShieldCheckIcon, TrashIcon } from "@heroicons/react/20/solid"; import { ShieldExclamationIcon } from "@heroicons/react/24/solid"; import { DialogClose } from "@radix-ui/react-dialog"; -import { Form, MetaFunction, useActionData, useFetcher } from "@remix-run/react"; -import { ActionFunction, LoaderFunctionArgs, json } from "@remix-run/server-runtime"; +import { Form, type MetaFunction, useActionData, useFetcher } from "@remix-run/react"; +import { type ActionFunction, type LoaderFunctionArgs, json } from "@remix-run/server-runtime"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { z } from "zod"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; @@ -16,7 +16,6 @@ import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "~/components import { Fieldset } from "~/components/primitives/Fieldset"; import { FormButtons } from "~/components/primitives/FormButtons"; import { FormError } from "~/components/primitives/FormError"; -import { Header2 } from "~/components/primitives/Headers"; import { Hint } from "~/components/primitives/Hint"; import { Input } from "~/components/primitives/Input"; import { InputGroup } from "~/components/primitives/InputGroup"; @@ -36,8 +35,8 @@ import { import { SimpleTooltip } from "~/components/primitives/Tooltip"; import { redirectWithSuccessMessage } from "~/models/message.server"; import { - CreatedPersonalAccessToken, - ObfuscatedPersonalAccessToken, + type CreatedPersonalAccessToken, + type ObfuscatedPersonalAccessToken, createPersonalAccessToken, getValidPersonalAccessTokens, revokePersonalAccessToken, @@ -53,7 +52,7 @@ export const meta: MetaFunction = () => { ]; }; -export const loader = async ({ request, params }: LoaderFunctionArgs) => { +export const loader = async ({ request }: LoaderFunctionArgs) => { const userId = await requireUserId(request); try { @@ -113,7 +112,7 @@ export const action: ActionFunction = async ({ request }) => { } case "revoke": { try { - await revokePersonalAccessToken(submission.value.tokenId); + await revokePersonalAccessToken(submission.value.tokenId, userId); return redirectWithSuccessMessage( personalAccessTokensPath(), @@ -125,6 +124,7 @@ export const action: ActionFunction = async ({ request }) => { } } default: { + submission.value satisfies never; return json({ errors: { body: "Invalid action" } }, { status: 400 }); } } diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.select-plan.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.select-plan.tsx index 8f511f0b08..8d9e19bfc6 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.select-plan.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.select-plan.tsx @@ -7,16 +7,16 @@ import { } from "@heroicons/react/20/solid"; import { ArrowDownCircleIcon, ArrowUpCircleIcon } from "@heroicons/react/24/outline"; import { Form, useLocation, useNavigation } from "@remix-run/react"; -import { ActionFunctionArgs } from "@remix-run/server-runtime"; +import { type ActionFunctionArgs } from "@remix-run/server-runtime"; import { uiComponent } from "@team-plain/typescript-sdk"; import { GitHubLightIcon } from "@trigger.dev/companyicons"; import { - FreePlanDefinition, - Limits, - PaidPlanDefinition, - Plans, - SetPlanBody, - SubscriptionResult, + type FreePlanDefinition, + type Limits, + type PaidPlanDefinition, + type Plans, + type SetPlanBody, + type SubscriptionResult, } from "@trigger.dev/platform"; import React, { useEffect, useState } from "react"; import { z } from "zod"; @@ -75,8 +75,8 @@ export async function action({ request, params }: ActionFunctionArgs) { message: message || undefined, }); - const organization = await prisma.organization.findUnique({ - where: { slug: organizationSlug }, + const organization = await prisma.organization.findFirst({ + where: { slug: organizationSlug, members: { some: { userId: user.id } } }, }); if (!organization) { diff --git a/apps/webapp/app/services/personalAccessToken.server.ts b/apps/webapp/app/services/personalAccessToken.server.ts index 80a251f657..ebe8bc31ff 100644 --- a/apps/webapp/app/services/personalAccessToken.server.ts +++ b/apps/webapp/app/services/personalAccessToken.server.ts @@ -79,15 +79,20 @@ export async function getPersonalAccessTokenFromAuthorizationCode(authorizationC }; } -export async function revokePersonalAccessToken(tokenId: string) { - await prisma.personalAccessToken.update({ +export async function revokePersonalAccessToken(tokenId: string, userId: string) { + const result = await prisma.personalAccessToken.updateMany({ where: { id: tokenId, + userId, }, data: { revokedAt: new Date(), }, }); + + if (result.count === 0) { + throw new Error("PAT not found or already revoked"); + } } export type PersonalAccessTokenAuthenticationResult = {