Skip to content

Commit 0ab3357

Browse files
committed
fix: use tokens for api url
1 parent 0d5ffc4 commit 0ab3357

File tree

7 files changed

+114
-141
lines changed

7 files changed

+114
-141
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
type DefaultDataProvider,
1616
RECORDS_PER_PAGE,
1717
} from "./default-data-provider";
18-
import { getConfig } from "@/components/lib/config";
1918

2019
const mightRequireAuth = __APP_TYPE__ === "engine";
2120

@@ -44,7 +43,7 @@ export function createClient(
4443
export const createGlobalContext = (opts: {
4544
engineToken: (() => string) | string;
4645
}) => {
47-
const client = createClient(getConfig().apiUrl, {
46+
const client = createClient(engineEnv().VITE_APP_API_URL, {
4847
token: opts.engineToken,
4948
});
5049
return {

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,8 @@ export default function CreateProjectFrameContent() {
4141
</Frame.Header>
4242
<Frame.Content>
4343
<Flex gap="4" direction="col">
44-
<ConnectVercelForm.Name />
44+
<ConnectVercelForm.Plan />
4545
<ConnectVercelForm.Endpoint />
46-
<ConnectVercelForm.Preview />
4746
</Flex>
4847
</Frame.Content>
4948
<Frame.Footer>
Lines changed: 5 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,22 @@
1-
import { faCopy, Icon } from "@rivet-gg/icons";
2-
import { useQuery } from "@tanstack/react-query";
3-
import { useParams } from "@tanstack/react-router";
41
import { type UseFormReturn, useFormContext } from "react-hook-form";
52
import z from "zod";
63
import {
7-
Button,
8-
CodePreview,
9-
CopyButton,
104
createSchemaForm,
115
FormControl,
6+
FormDescription,
127
FormField,
138
FormItem,
149
FormLabel,
1510
FormMessage,
1611
Input,
17-
Label,
18-
ScrollArea,
1912
Select,
2013
SelectContent,
2114
SelectItem,
2215
SelectTrigger,
2316
SelectValue,
2417
} from "@/components";
25-
import { useCloudDataProvider } from "@/components/actors";
2618

2719
export const formSchema = z.object({
28-
name: z
29-
.string()
30-
.max(16)
31-
.refine((value) => value.trim() !== "" && value.trim() === value, {
32-
message: "Name cannot be empty or contain whitespaces",
33-
}),
3420
plan: z.string(),
3521
endpoint: z.string().url(),
3622
});
@@ -44,29 +30,6 @@ export type SubmitHandler = (
4430
const { Form, Submit, SetValue } = createSchemaForm(formSchema);
4531
export { Form, Submit, SetValue };
4632

47-
export const Name = ({ className }: { className?: string }) => {
48-
const { control } = useFormContext<FormValues>();
49-
return (
50-
<FormField
51-
control={control}
52-
name="name"
53-
render={({ field }) => (
54-
<FormItem className={className}>
55-
<FormLabel className="col-span-1">Name</FormLabel>
56-
<FormControl className="row-start-2">
57-
<Input
58-
placeholder="Enter a runner name..."
59-
maxLength={25}
60-
{...field}
61-
/>
62-
</FormControl>
63-
<FormMessage className="col-span-1" />
64-
</FormItem>
65-
)}
66-
/>
67-
);
68-
};
69-
7033
export const Plan = ({ className }: { className?: string }) => {
7134
const { control } = useFormContext<FormValues>();
7235
return (
@@ -75,17 +38,14 @@ export const Plan = ({ className }: { className?: string }) => {
7538
name="plan"
7639
render={({ field }) => (
7740
<FormItem className={className}>
78-
<FormLabel className="col-span-1">Plan</FormLabel>
41+
<FormLabel className="col-span-1">Vercel Plan</FormLabel>
7942
<FormControl className="row-start-2">
8043
<Select
8144
onValueChange={field.onChange}
8245
value={field.value}
8346
>
84-
<SelectTrigger
85-
variant="ghost"
86-
className="h-full pr-2 rounded-none"
87-
>
88-
<SelectValue placeholder="Select table or view..." />
47+
<SelectTrigger>
48+
<SelectValue placeholder="Select your Vercel plan..." />
8949
</SelectTrigger>
9050
<SelectContent>
9151
<SelectItem value="hobby">Hobby</SelectItem>
@@ -96,6 +56,7 @@ export const Plan = ({ className }: { className?: string }) => {
9656
</SelectContent>
9757
</Select>
9858
</FormControl>
59+
<FormDescription className="col-span-1"></FormDescription>
9960
<FormMessage className="col-span-1" />
10061
</FormItem>
10162
)}
@@ -127,60 +88,3 @@ export const Endpoint = ({ className }: { className?: string }) => {
12788
/>
12889
);
12990
};
130-
131-
const code = ({
132-
token,
133-
name,
134-
}: {
135-
token: string;
136-
name: string;
137-
}) => `import { registry } from "./registry";
138-
139-
registry.runServer({
140-
token: "${token}",
141-
name: "${name}",
142-
});`;
143-
144-
export function Preview() {
145-
const params = useParams({
146-
from: "/_context/_cloud/orgs/$organization/projects/$project/ns/$namespace",
147-
});
148-
const { data } = useQuery(
149-
useCloudDataProvider().currentOrgProjectNamespaceQueryOptions(params),
150-
);
151-
152-
const { watch } = useFormContext<FormValues>();
153-
const name = watch("name");
154-
return (
155-
<div className="space-y-2">
156-
<Label>Code</Label>
157-
<div className="text-xs border rounded-md p-2 relative w-full">
158-
<ScrollArea>
159-
<CodePreview
160-
className="w-full min-w-0"
161-
language="typescript"
162-
code={code({
163-
token: data?.access.token || "<TOKEN>",
164-
name,
165-
})}
166-
/>
167-
</ScrollArea>
168-
169-
<CopyButton
170-
value={code({
171-
token: data?.access.token || "<TOKEN>",
172-
name,
173-
})}
174-
>
175-
<Button
176-
variant="secondary"
177-
size="icon-sm"
178-
className="absolute top-1 right-2"
179-
>
180-
<Icon icon={faCopy} />
181-
</Button>
182-
</CopyButton>
183-
</div>
184-
</div>
185-
);
186-
}

frontend/src/components/actors/guard-connectable-inspector.tsx

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import { faPowerOff, faSpinnerThird, Icon } from "@rivet-gg/icons";
22
import { useMutation, useQuery, useSuspenseQuery } from "@tanstack/react-query";
33
import { useMatch } from "@tanstack/react-router";
4-
import { createContext, type ReactNode, useContext, useMemo } from "react";
4+
import {
5+
createContext,
6+
type ReactNode,
7+
useContext,
8+
useMemo,
9+
useState,
10+
} from "react";
511
import { useInspectorCredentials } from "@/app/credentials-context";
612
import { createInspectorActorContext } from "@/queries/actor-inspector";
713
import { DiscreteCopyButton } from "../copy-area";
@@ -33,6 +39,12 @@ export function GuardConnectableInspector({
3339
} = useQuery({
3440
...useDataProvider().actorQueryOptions(actorId),
3541
refetchInterval: 1000,
42+
select: (data) => ({
43+
destroyedAt: data.destroyedAt,
44+
sleepingAt: data.sleepingAt,
45+
pendingAllocationAt: data.pendingAllocationAt,
46+
startedAt: data.startedAt,
47+
}),
3648
});
3749

3850
if (destroyedAt) {
@@ -126,7 +138,7 @@ function ActorInspectorProvider({ children }: { children: ReactNode }) {
126138
}
127139

128140
function useActorRunner({ actorId }: { actorId: ActorId }) {
129-
const { data: actor } = useSuspenseQuery(
141+
const { data: actor, isLoading } = useSuspenseQuery(
130142
useDataProvider().actorQueryOptions(actorId),
131143
);
132144

@@ -142,19 +154,29 @@ function useActorRunner({ actorId }: { actorId: ActorId }) {
142154
throw new Error("Actor is missing required fields");
143155
}
144156

145-
const { data: runner } = useQuery({
157+
const {
158+
data: runner,
159+
isLoading: isLoadingRunner,
160+
isSuccess,
161+
} = useQuery({
146162
...useEngineCompatDataProvider().runnerByNameQueryOptions({
147163
runnerName: actor.runner,
148164
namespace: match.params.namespace,
149165
}),
166+
retryDelay: 10_000,
150167
refetchInterval: 1000,
151168
});
152169

153-
return { actor, runner };
170+
return {
171+
actor,
172+
runner,
173+
isLoading: isLoading || isLoadingRunner,
174+
isSuccess,
175+
};
154176
}
155177

156178
function useActorEngineContext({ actorId }: { actorId: ActorId }) {
157-
const { actor, runner } = useActorRunner({ actorId });
179+
const { actor, runner, isLoading } = useActorRunner({ actorId });
158180

159181
const actorContext = useMemo(() => {
160182
return createInspectorActorContext({
@@ -164,7 +186,7 @@ function useActorEngineContext({ actorId }: { actorId: ActorId }) {
164186
});
165187
}, [runner?.metadata?.inspectorToken]);
166188

167-
return { actorContext, actor, runner };
189+
return { actorContext, actor, runner, isLoading };
168190
}
169191

170192
function ActorEngineProvider({
@@ -174,7 +196,9 @@ function ActorEngineProvider({
174196
actorId: ActorId;
175197
children: ReactNode;
176198
}) {
177-
const { actorContext, actor, runner } = useActorEngineContext({ actorId });
199+
const { actorContext, actor, runner } = useActorEngineContext({
200+
actorId,
201+
});
178202

179203
if (!runner || !actor.runner) {
180204
return (
@@ -207,6 +231,7 @@ function NoRunnerInfo({ runner }: { runner: string }) {
207231
<span className="font-mono-console">{runner}</span>
208232
</DiscreteCopyButton>
209233
</p>
234+
<p>Will retry automatically in the background.</p>
210235
</Info>
211236
);
212237
}
@@ -232,22 +257,24 @@ function WakeUpActorButton({ actorId }: { actorId: ActorId }) {
232257
}
233258

234259
function AutoWakeUpActor({ actorId }: { actorId: ActorId }) {
235-
const { runner, actor, actorContext } = useActorEngineContext({ actorId });
260+
const { runner, actor, actorContext } = useActorEngineContext({
261+
actorId,
262+
});
236263

237-
const { isPending } = useQuery(
264+
useQuery(
238265
actorContext.actorAutoWakeUpQueryOptions(actorId, {
239266
enabled: !!runner,
240267
}),
241268
);
242269

243270
if (!runner) return <NoRunnerInfo runner={actor.runner || "unknown"} />;
244271

245-
return isPending ? (
272+
return (
246273
<Info>
247274
<div className="flex items-center">
248275
<Icon icon={faSpinnerThird} className="animate-spin mr-2" />
249276
Waiting for Actor to wake...
250277
</div>
251278
</Info>
252-
) : null;
279+
);
253280
}

frontend/src/components/lib/config.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ export const ConfigContext = createContext<Config>({
2323
export const useConfig = () => useContext(ConfigContext);
2424
export const ConfigProvider = ConfigContext.Provider;
2525

26-
const getApiEndpoint = (apiEndpoint: string) => {
27-
if (apiEndpoint === "__AUTO__") {
28-
if (location.hostname.startsWith("hub.")) {
26+
export const getApiEndpoint = (apiEndpoint: string) => {
27+
if (typeof window !== "undefined" && apiEndpoint === "__AUTO__") {
28+
if (window.location.hostname.startsWith("hub.")) {
2929
// Connect to the corresponding API endpoint
30-
return `https://${location.hostname.replace("hub.", "api.")}`;
30+
return `https://${window.location.hostname.replace("hub.", "api.")}`;
3131
}
3232
// Default to staging servers for all other endpoints
3333
return "https://api.staging2.gameinc.io";
34-
} else if (apiEndpoint === "__SAME__") {
35-
return location.origin;
34+
} else if (typeof window !== "undefined" && apiEndpoint === "__SAME__") {
35+
return window.location.origin;
3636
}
3737
return apiEndpoint;
3838
};

frontend/src/lib/env.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import z from "zod";
2+
import { getApiEndpoint } from "../components/lib/config";
23

34
export const commonEnvSchema = z.object({
4-
VITE_APP_API_URL: z.string(),
5+
VITE_APP_API_URL: z.string().transform((url) => {
6+
return getApiEndpoint(url);
7+
}),
58
VITE_APP_ASSETS_URL: z.string().url(),
69
VITE_APP_POSTHOG_API_KEY: z.string().optional(),
710
VITE_APP_POSTHOG_API_HOST: z.string().url().optional(),
@@ -19,7 +22,9 @@ export const engineEnv = () => commonEnvSchema.parse(import.meta.env);
1922

2023
export const cloudEnvSchema = commonEnvSchema.merge(
2124
z.object({
22-
VITE_APP_API_URL: z.string().url(),
25+
VITE_APP_API_URL: z.string().transform((url) => {
26+
return getApiEndpoint(url);
27+
}),
2328
VITE_APP_CLOUD_ENGINE_URL: z.string().url(),
2429
VITE_APP_CLOUD_API_URL: z.string().url(),
2530
VITE_APP_CLERK_PUBLISHABLE_KEY: z.string(),

0 commit comments

Comments
 (0)