Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 2 additions & 21 deletions frontend/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
Toaster,
TooltipProvider,
} from "@/components";
import { NotFoundCard } from "./app/not-found-card";
import { RouteLayout } from "./app/route-layout";
import { RootLayout } from "./components/layout";
import { clerk } from "./lib/auth";
Expand Down Expand Up @@ -60,27 +61,7 @@ export const router = createRouter({
},
defaultNotFoundComponent: () => (
<RouteLayout>
<div className="bg-card h-full border my-2 mr-2 rounded-lg">
<div className="mt-2 flex flex-col items-center justify-center h-full">
<div className="w-full sm:w-96">
<Card>
<CardHeader>
<CardTitle className="flex items-center">
404
</CardTitle>
<CardDescription>
The page was not found
</CardDescription>
</CardHeader>
<CardContent>
<Button asChild variant="secondary">
<Link to="/">Go home</Link>
</Button>
</CardContent>
</Card>
</div>
</div>
</div>
<NotFoundCard />
</RouteLayout>
),
});
Expand Down
24 changes: 24 additions & 0 deletions frontend/src/app/data-providers/cloud-data-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -381,5 +381,29 @@ export const createNamespaceContext = ({
},
});
},
publishableTokenQueryOptions() {
return queryOptions({
staleTime: 5 * 60 * 1000, // 5 minutes
gcTime: 5 * 60 * 1000, // 5 minutes
queryKey: [
{
namespace,
project: parent.project,
organization: parent.organization,
},
"tokens",
"publishable",
],
queryFn: async () => {
const f = parent.client.namespaces.createPublishableToken(
parent.project,
namespace,
{ org: parent.organization },
);
const t = await f;
return t.token;
},
});
},
};
};
1 change: 1 addition & 0 deletions frontend/src/app/data-providers/default-data-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ const defaultContext = {
: null,
sleepingAt: data.sleepingAt ? new Date(data.sleepingAt) : null,
region: data.region,
runner: data.runner,
crashPolicy: data.crashPolicy,
}),
});
Expand Down
103 changes: 103 additions & 0 deletions frontend/src/app/dialogs/tokens-frame.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { faQuestionCircle, faRailway, Icon } from "@rivet-gg/icons";
import { useQuery } from "@tanstack/react-query";
import { useRouteContext } from "@tanstack/react-router";
import * as ConnectRailwayForm from "@/app/forms/connect-railway-form";
import { HelpDropdown } from "@/app/help-dropdown";
import {
Button,
type DialogContentProps,
DiscreteInput,
Frame,
Label,
Skeleton,
} from "@/components";
import {
useCloudDataProvider,
useEngineCompatDataProvider,
} from "@/components/actors";
import { defineStepper } from "@/components/ui/stepper";
import { cloudEnv, engineEnv } from "@/lib/env";

interface TokensFrameContentProps extends DialogContentProps {}

export default function TokensFrameContent({
onClose,
}: TokensFrameContentProps) {
return (
<>
<Frame.Header>
<Frame.Title className="gap-2 flex items-center">
<div>Namespace Tokens</div>
<HelpDropdown>
<Button variant="ghost" size="icon">
<Icon icon={faQuestionCircle} />
</Button>
</HelpDropdown>
</Frame.Title>
<Frame.Description>
These tokens are used to authenticate requests to the Rivet
Engine API. Keep them secret!
</Frame.Description>
</Frame.Header>
<Frame.Content>
<div className="items-center grid grid-cols-1 gap-6">
<SecretToken />
<PublishableToken />
</div>
</Frame.Content>
<Frame.Footer>
<Button variant="secondary" onClick={onClose}>
Close
</Button>
</Frame.Footer>
</>
);
}

function SecretToken() {
const dataProvider = useRouteContext({
from: "/_context/_cloud/orgs/$organization/projects/$project/ns/$namespace",
select: (c) => c.dataProvider,
});
const { data, isLoading } = useQuery(
dataProvider.engineAdminTokenQueryOptions(),
);
return (
<div className="space-y-2">
<Label>Secret Token</Label>
{isLoading ? (
<Skeleton className="w-56 h-10" />
) : (
<DiscreteInput value={data || ""} />
)}
<p className="text-sm text-muted-foreground">
Only use in secure server environments. Grants full access to
your namespace. Used to connect your Runners to your namespace.
</p>
</div>
);
}

function PublishableToken() {
const dataProvider = useRouteContext({
from: "/_context/_cloud/orgs/$organization/projects/$project/ns/$namespace",
select: (c) => c.dataProvider,
});
const { data, isLoading } = useQuery(
dataProvider.publishableTokenQueryOptions(),
);
return (
<div className="space-y-2">
<Label>Publishable Token</Label>
{isLoading ? (
<Skeleton className="w-56 h-10" />
) : (
<DiscreteInput value={data || ""} />
)}
<p className="text-sm text-muted-foreground">
Safe to use in public contexts like client-side code. Allows
your frontend to interact with Rivet services.
</p>
</div>
);
}
35 changes: 35 additions & 0 deletions frontend/src/app/not-found-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Link } from "@tanstack/react-router";
import {
Button,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components";

export function NotFoundCard() {
return (
<div className="bg-card h-full border my-2 mr-2 rounded-lg">
<div className="mt-2 flex flex-col items-center justify-center h-full">
<div className="w-full sm:w-96">
<Card>
<CardHeader>
<CardTitle className="flex items-center">
404
</CardTitle>
<CardDescription>
The page was not found
</CardDescription>
</CardHeader>
<CardContent>
<Button asChild variant="secondary">
<Link to="/">Go home</Link>
</Button>
</CardContent>
</Card>
</div>
</div>
</div>
);
}
1 change: 1 addition & 0 deletions frontend/src/app/use-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export const useDialog = {
ProvideEngineCredentials: createDialogHook(
() => import("@/app/dialogs/provide-engine-credentials-frame"),
),
Tokens: createDialogHook(() => import("@/app/dialogs/tokens-frame")),
};
10 changes: 10 additions & 0 deletions frontend/src/app/user-dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ export function UserDropdown() {
>
Profile
</DropdownMenuItem>
<DropdownMenuItem
onSelect={() => {
navigate({
to: ".",
search: { ...params, modal: "tokens" },
});
}}
>
Tokens
</DropdownMenuItem>
{clerk.organization ? (
<DropdownMenuItem
onSelect={() => {
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/components/actors/actor-general.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function ActorGeneral({ actorId }: ActorGeneralProps) {
pendingAllocationAt,
sleepingAt,
crashPolicy,
runner,
} = {},
} = useQuery(useDataProvider().actorGeneralQueryOptions(actorId));

Expand Down Expand Up @@ -61,6 +62,8 @@ export function ActorGeneral({ actorId }: ActorGeneralProps) {
/>
</Flex>
</Dd>
<Dt>Runner</Dt>
<Dd>{runner}</Dd>
<Dt>Crash Policy</Dt>
<Dd>{crashPolicy}</Dd>
<Dt>Created</Dt>
Expand Down
18 changes: 18 additions & 0 deletions frontend/src/routes/_context/_cloud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ function CloudModals() {
const CreateNamespaceDialog = useDialog.CreateNamespace.Dialog;
const ConnectVercelDialog = useDialog.ConnectVercel.Dialog;
const ConnectRailwayDialog = useDialog.ConnectRailway.Dialog;
const TokensDialog = useDialog.Tokens.Dialog;

return (
<>
Expand Down Expand Up @@ -116,6 +117,23 @@ function CloudModals() {
},
}}
/>
<TokensDialog
dialogProps={{
open: search.modal === "tokens",
// FIXME
onOpenChange: (value: any) => {
if (!value) {
navigate({
to: ".",
search: (old) => ({
...old,
modal: undefined,
}),
});
}
},
}}
/>
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createFileRoute, Outlet } from "@tanstack/react-router";
import { match } from "ts-pattern";
import { createProjectContext } from "@/app/data-providers/cloud-data-provider";
import { NotFoundCard } from "@/app/not-found-card";
import { RouteError } from "@/app/route-error";
import { useDialog } from "@/app/use-dialog";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createFileRoute } from "@tanstack/react-router";
import { createNamespaceContext } from "@/app/data-providers/cloud-data-provider";
import { NotFoundCard } from "@/app/not-found-card";
import { RouteLayout } from "@/app/route-layout";

export const Route = createFileRoute(
Expand Down Expand Up @@ -28,6 +29,7 @@ export const Route = createFileRoute(
},
};
},
notFoundComponent: () => <NotFoundCard />,
});

function RouteComponent() {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/routes/_context/_engine/ns.$namespace.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createFileRoute } from "@tanstack/react-router";
import { match } from "ts-pattern";
import { createNamespaceContext } from "@/app/data-providers/engine-data-provider";
import { NotFoundCard } from "@/app/not-found-card";
import { RouteLayout } from "@/app/route-layout";

export const Route = createFileRoute("/_context/_engine/ns/$namespace")({
Expand All @@ -20,6 +21,7 @@ export const Route = createFileRoute("/_context/_engine/ns/$namespace")({
});
},
component: RouteComponent,
notFoundComponent: () => <NotFoundCard />,
});

function RouteComponent() {
Expand Down
Loading