Skip to content

Commit 5721bf4

Browse files
committed
feat: railwa connection frame
1 parent f0642e3 commit 5721bf4

File tree

10 files changed

+303
-137
lines changed

10 files changed

+303
-137
lines changed

frontend/src/app/data-providers/cloud-data-provider.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ export const createOrganizationContext = ({
171171
};
172172

173173
return {
174+
organization,
174175
orgProjectNamespacesQueryOptions,
175176
currentOrgProjectNamespacesQueryOptions: (opts: {
176177
project: string;
@@ -233,6 +234,7 @@ export const createProjectContext = ({
233234
} & ReturnType<typeof createOrganizationContext> &
234235
ReturnType<typeof createGlobalContext>) => {
235236
return {
237+
project,
236238
createNamespaceMutationOptions(opts: {
237239
onSuccess?: (data: Namespace) => void;
238240
}) {
@@ -355,5 +357,29 @@ export const createNamespaceContext = ({
355357
namespaceQueryOptions() {
356358
return parent.currentProjectNamespaceQueryOptions({ namespace });
357359
},
360+
connectRunnerTokenQueryOptions() {
361+
return queryOptions({
362+
staleTime: 5 * 60 * 1000, // 5 minutes
363+
gcTime: 5 * 60 * 1000, // 5 minutes
364+
queryKey: [
365+
{
366+
namespace,
367+
project: parent.project,
368+
organization: parent.organization,
369+
},
370+
"runners",
371+
"connect",
372+
],
373+
queryFn: async () => {
374+
const f = parent.client.namespaces.createPublishableToken(
375+
parent.project,
376+
namespace,
377+
{ org: parent.organization },
378+
);
379+
const t = await f;
380+
return t.token;
381+
},
382+
});
383+
},
358384
};
359385
};

frontend/src/app/data-providers/engine-data-provider.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type Rivet, RivetClient } from "@rivetkit/engine-api-full";
22
import { infiniteQueryOptions, queryOptions } from "@tanstack/react-query";
3+
import { getConfig, ls } from "@/components";
34
import {
45
type Actor,
56
ActorFeature,
@@ -551,6 +552,19 @@ export const createNamespaceContext = ({
551552
},
552553
});
553554
},
555+
connectRunnerTokenQueryOptions() {
556+
return queryOptions({
557+
staleTime: 1000,
558+
gcTime: 1000,
559+
queryKey: [{ namespace }, "runners", "connect"],
560+
queryFn: async () => {
561+
return ls.engineCredentials.get(getConfig().apiUrl) || "";
562+
},
563+
meta: {
564+
mightRequireAuth,
565+
},
566+
});
567+
},
554568
};
555569
};
556570

Lines changed: 159 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,42 @@
11
import { faQuestionCircle, faRailway, Icon } from "@rivet-gg/icons";
2+
import { useQuery } from "@tanstack/react-query";
23
import * as ConnectRailwayForm from "@/app/forms/connect-railway-form";
34
import { HelpDropdown } from "@/app/help-dropdown";
4-
import { Button, Flex, Frame } from "@/components";
5+
import {
6+
Button,
7+
type DialogContentProps,
8+
DiscreteInput,
9+
Frame,
10+
Skeleton,
11+
} from "@/components";
12+
import { useEngineCompatDataProvider } from "@/components/actors";
13+
import { defineStepper } from "@/components/ui/stepper";
14+
import { engineEnv } from "@/lib/env";
515

6-
export default function CreateProjectFrameContent() {
16+
const { Stepper } = defineStepper(
17+
{
18+
id: "step-1",
19+
title: "Deploy to Railway",
20+
},
21+
{
22+
id: "step-2",
23+
title: "Set Environment Variables",
24+
},
25+
{
26+
id: "step-3",
27+
title: "Confirm Connection",
28+
},
29+
);
30+
31+
interface ConnectRailwayFrameContentProps extends DialogContentProps {}
32+
33+
export default function ConnectRailwayFrameContent({
34+
onClose,
35+
}: ConnectRailwayFrameContentProps) {
736
return (
837
<ConnectRailwayForm.Form
938
onSubmit={async () => {}}
10-
defaultValues={{ name: "" }}
39+
defaultValues={{ endpoint: "" }}
1140
>
1241
<Frame.Header>
1342
<Frame.Title className="justify-between flex items-center">
@@ -22,12 +51,134 @@ export default function CreateProjectFrameContent() {
2251
</Frame.Title>
2352
</Frame.Header>
2453
<Frame.Content>
25-
<Flex gap="4" direction="col">
26-
<ConnectRailwayForm.Name />
27-
<ConnectRailwayForm.Preview />
28-
<ConnectRailwayForm.ConnectionCheck />
29-
</Flex>
54+
<FormStepper onClose={onClose} />
3055
</Frame.Content>
3156
</ConnectRailwayForm.Form>
3257
);
3358
}
59+
60+
function FormStepper({ onClose }: { onClose?: () => void }) {
61+
return (
62+
<Stepper.Provider variant="vertical">
63+
{({ methods }) => (
64+
<>
65+
<Stepper.Navigation>
66+
{methods.all.map((step) => (
67+
<Stepper.Step
68+
key={step.id}
69+
className="min-w-0"
70+
of={step.id}
71+
onClick={() => methods.goTo(step.id)}
72+
>
73+
<Stepper.Title>{step.title}</Stepper.Title>
74+
{methods.when(step.id, (step) => {
75+
return (
76+
<Stepper.Panel className="space-y-4">
77+
{step.id === "step-1" && (
78+
<>
79+
<p>
80+
Deploy any RivetKit app
81+
to Railway.
82+
</p>
83+
<p>
84+
Or use our Railway
85+
template to get started
86+
quickly.
87+
</p>
88+
<a
89+
href="https://railway.com/deploy/rivet?referralCode=RC7bza&utm_medium=integration&utm_source=template&utm_campaign=generic"
90+
target="_blank"
91+
rel="noreferrer"
92+
className="inline-block h-10"
93+
>
94+
<img
95+
height={40}
96+
src="https://railway.com/button.svg"
97+
alt="Deploy to Railway"
98+
/>
99+
</a>
100+
101+
<p>
102+
After deploying your app
103+
to Railway, return here
104+
to add the endpoint.
105+
</p>
106+
</>
107+
)}
108+
{step.id === "step-2" && (
109+
<EnvVariablesStep />
110+
)}
111+
{step.id === "step-3" && (
112+
<div>
113+
<ConnectRailwayForm.Endpoint
114+
placeholder="https://your-application.up.railway.app/"
115+
className="mb-2"
116+
/>
117+
<ConnectRailwayForm.ConnectionCheck />
118+
</div>
119+
)}
120+
<Stepper.Controls>
121+
<Button
122+
type="button"
123+
variant="secondary"
124+
onClick={methods.prev}
125+
disabled={methods.isFirst}
126+
>
127+
Previous
128+
</Button>
129+
<Button
130+
onClick={
131+
methods.isLast
132+
? onClose
133+
: methods.next
134+
}
135+
>
136+
{methods.isLast
137+
? "Done"
138+
: "Next"}
139+
</Button>
140+
</Stepper.Controls>
141+
</Stepper.Panel>
142+
);
143+
})}
144+
</Stepper.Step>
145+
))}
146+
</Stepper.Navigation>
147+
</>
148+
)}
149+
</Stepper.Provider>
150+
);
151+
}
152+
153+
function EnvVariablesStep() {
154+
const dataProvider = useEngineCompatDataProvider();
155+
156+
const { data, isLoading } = useQuery(
157+
dataProvider.connectRunnerTokenQueryOptions(),
158+
);
159+
160+
return (
161+
<>
162+
<p>
163+
Set the following environment variables in your Railway project
164+
settings.
165+
</p>
166+
<div className="gap-1 items-center">
167+
{__APP_TYPE__ === "engine" ? (
168+
<div className="flex gap-1 items-center border-b border-b-border/40 pb-2 mb-2">
169+
<DiscreteInput value="RIVET_ENGINE" show />
170+
<DiscreteInput value={engineEnv().VITE_APP_API_URL} />
171+
</div>
172+
) : null}
173+
<div className="flex gap-1 items-center">
174+
<DiscreteInput value="RIVET_TOKEN" show />
175+
{isLoading ? (
176+
<Skeleton className="w-56 h-10" />
177+
) : (
178+
<DiscreteInput value={data || ""} />
179+
)}
180+
</div>
181+
</div>
182+
</>
183+
);
184+
}

frontend/src/app/dialogs/connect-vercel-frame.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import {
55
Button,
66
type DialogContentProps,
77
Frame,
8-
Step,
9-
Steps,
108
} from "@/components";
119
import { defineStepper } from "@/components/ui/stepper";
1210

@@ -25,7 +23,7 @@ const { Stepper } = defineStepper(
2523
},
2624
{
2725
id: "step-4",
28-
title: "Add Vercel Endpoint",
26+
title: "Confirm Connection",
2927
},
3028
);
3129

@@ -69,7 +67,7 @@ function FormStepper({ onClose }: { onClose?: () => void }) {
6967
<Stepper.Navigation>
7068
{methods.all.map((step) => (
7169
<Stepper.Step
72-
className="min-w-0"
70+
className="min-w-0"
7371
of={step.id}
7472
onClick={() => methods.goTo(step.id)}
7573
>

0 commit comments

Comments
 (0)