Skip to content

Commit 746212f

Browse files
committed
feat(fe): handle runners unavailability
1 parent e03d55f commit 746212f

File tree

8 files changed

+346
-160
lines changed

8 files changed

+346
-160
lines changed

frontend/src/components/actors/actor-filters-context.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
import { faHashtag, faKey } from "@rivet-gg/icons";
2-
import { useQuery } from "@tanstack/react-query";
32
import { useSearch } from "@tanstack/react-router";
4-
import { CommandGroup, CommandItem } from "cmdk";
53
import { createContext, useContext } from "react";
6-
import { cn } from "../lib/utils";
7-
import { Checkbox } from "../ui/checkbox";
84
import {
95
createFiltersPicker,
106
createFiltersRemover,
117
createFiltersSchema,
128
type FilterDefinitions,
139
FilterOp,
14-
type OptionsProviderProps,
10+
type PickFiltersOptions,
1511
} from "../ui/filters";
16-
import { ActorRegion } from "./actor-region";
17-
import { ActorStatus } from "./actor-status";
18-
import { useManager } from "./manager-context";
19-
import type { ActorStatus as ActorStatusType } from "./queries";
2012

2113
export const ACTORS_FILTERS_DEFINITIONS = {
2214
id: {
@@ -48,6 +40,13 @@ export const ACTORS_FILTERS_DEFINITIONS = {
4840
category: "display",
4941
ephemeral: true,
5042
},
43+
wakeOnSelect: {
44+
type: "boolean",
45+
label: "Auto-wake Actors on select",
46+
category: "display",
47+
ephemeral: true,
48+
defaultValue: ["1"],
49+
},
5150
// tags: {
5251
// type: "select",
5352
// label: "Tags",
@@ -143,3 +142,11 @@ export const useFilters = (
143142
select: (state) => fn(pick(state)),
144143
});
145144
};
145+
146+
export function useFiltersValue(opts: PickFiltersOptions = {}) {
147+
const { pick } = useActorsFilters();
148+
return useSearch({
149+
from: "/_layout",
150+
select: (state) => pick(state, opts),
151+
});
152+
}

frontend/src/components/actors/actor-queries-context.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,44 @@ export const defaultActorContext = {
208208
},
209209
};
210210
},
211+
212+
actorWakeUpMutationOptions(actorId: ActorId) {
213+
return {
214+
mutationKey: ["actor", actorId, "wake-up"],
215+
mutationFn: async () => {
216+
const client = this.createActorInspector(actorId);
217+
try {
218+
await client.ping.$get();
219+
return true;
220+
} catch {
221+
return false;
222+
}
223+
},
224+
};
225+
},
226+
227+
actorAutoWakeUpQueryOptions(
228+
actorId: ActorId,
229+
{ enabled }: { enabled?: boolean } = {},
230+
) {
231+
return queryOptions({
232+
enabled,
233+
refetchInterval: 1000,
234+
staleTime: 0,
235+
gcTime: 0,
236+
queryKey: ["actor", actorId, "auto-wake-up"],
237+
queryFn: async ({ queryKey: [, actorId] }) => {
238+
const client = this.createActorInspector(actorId);
239+
try {
240+
await client.ping.$get();
241+
return true;
242+
} catch {
243+
return false;
244+
}
245+
},
246+
retry: false,
247+
});
248+
},
211249
};
212250

213251
export type ActorContext = typeof defaultActorContext;

frontend/src/components/actors/actor-state-tab.tsx

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,21 @@ import { Button } from "../ui/button";
55
import { ActorEditableState } from "./actor-editable-state";
66
import { useActor } from "./actor-queries-context";
77
import { useActorsView } from "./actors-view-context-provider";
8-
import { useManager } from "./manager-context";
98
import type { ActorId } from "./queries";
109

1110
interface ActorStateTabProps {
1211
actorId: ActorId;
1312
}
1413

1514
export function ActorStateTab({ actorId }: ActorStateTabProps) {
16-
const { data: destroyedAt } = useQuery(
17-
useManager().actorDestroyedAtQueryOptions(actorId),
18-
);
19-
2015
const { links } = useActorsView();
2116

2217
const actorQueries = useActor();
2318
const {
2419
data: state,
2520
isError,
2621
isLoading,
27-
} = useQuery(
28-
actorQueries.actorStateQueryOptions(actorId, { enabled: !destroyedAt }),
29-
);
30-
31-
if (destroyedAt) {
32-
return <Info>State Preview is unavailable for inactive Actors.</Info>;
33-
}
22+
} = useQuery(actorQueries.actorStateQueryOptions(actorId));
3423

3524
if (isError) {
3625
return (
@@ -69,7 +58,7 @@ export function ActorStateTab({ actorId }: ActorStateTabProps) {
6958

7059
export function Info({ children }: PropsWithChildren) {
7160
return (
72-
<div className="flex-1 flex flex-col gap-2 items-center justify-center h-full text-center">
61+
<div className="flex-1 flex flex-col gap-2 items-center justify-center h-full text-center max-w-md mx-auto">
7362
{children}
7463
</div>
7564
);

frontend/src/components/actors/actors-actor-details.tsx

Lines changed: 43 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import { faQuestionSquare, Icon } from "@rivet-gg/icons";
2-
import {
3-
useQuery,
4-
useSuspenseInfiniteQuery,
5-
useSuspenseQuery,
6-
} from "@tanstack/react-query";
7-
import { useMatch } from "@tanstack/react-router";
8-
import { memo, type ReactNode, Suspense, useMemo } from "react";
9-
import { useInspectorCredentials } from "@/app/credentials-context";
2+
import { useQuery } from "@tanstack/react-query";
3+
import { memo, type ReactNode, Suspense } from "react";
104
import {
115
cn,
126
Flex,
@@ -15,26 +9,20 @@ import {
159
TabsList,
1610
TabsTrigger,
1711
} from "@/components";
18-
import { createEngineActorContext } from "@/queries/actor-engine";
19-
import { createInspectorActorContext } from "@/queries/actor-inspector";
20-
import {
21-
type NamespaceNameId,
22-
runnersQueryOptions,
23-
} from "@/queries/manager-engine";
2412
import { ActorConfigTab } from "./actor-config-tab";
2513
import { ActorConnectionsTab } from "./actor-connections-tab";
2614
import { ActorDatabaseTab } from "./actor-db-tab";
2715
import { ActorDetailsSettingsProvider } from "./actor-details-settings";
2816
import { ActorEventsTab } from "./actor-events-tab";
2917
import { ActorLogsTab } from "./actor-logs-tab";
3018
import { ActorMetricsTab } from "./actor-metrics-tab";
31-
import { ActorProvider } from "./actor-queries-context";
3219
import { ActorStateTab } from "./actor-state-tab";
3320
import { QueriedActorStatus } from "./actor-status";
3421
import { ActorStopButton } from "./actor-stop-button";
3522
import { ActorsSidebarToggleButton } from "./actors-sidebar-toggle-button";
3623
import { useActorsView } from "./actors-view-context-provider";
3724
import { ActorConsole } from "./console/actor-console";
25+
import { GuardConnectableInspector } from "./guard-connectable-inspector";
3826
import { useManager } from "./manager-context";
3927
import { ActorFeature, type ActorId } from "./queries";
4028
import { ActorWorkerContextProvider } from "./worker/actor-worker-context";
@@ -57,34 +45,32 @@ export const ActorsActorDetails = memo(
5745
useManager().actorFeaturesQueryOptions(actorId),
5846
);
5947

60-
const supportsConsole = features?.includes(ActorFeature.Console);
48+
const supportsConsole = false;
6149

6250
return (
63-
<ActorContextProvider actorId={actorId}>
64-
<ActorDetailsSettingsProvider>
65-
<ActorWorkerContextProvider
51+
<ActorDetailsSettingsProvider>
52+
<div className="flex flex-col h-full flex-1">
53+
<ActorTabs
54+
features={features}
6655
actorId={actorId}
67-
// notifyOnReconnect={features?.includes(
68-
// ActorFeature.InspectReconnectNotification,
69-
// )}
70-
>
71-
<div className="flex flex-col h-full flex-1">
72-
<ActorTabs
73-
features={features}
74-
actorId={actorId}
75-
tab={tab}
76-
onTabChange={onTabChange}
77-
// onExportLogs={onExportLogs}
78-
// isExportingLogs={isExportingLogs}
79-
/>
56+
tab={tab}
57+
onTabChange={onTabChange}
58+
// onExportLogs={onExportLogs}
59+
// isExportingLogs={isExportingLogs}
60+
/>
8061

81-
{supportsConsole ? (
82-
<ActorConsole actorId={actorId} />
83-
) : null}
84-
</div>
85-
</ActorWorkerContextProvider>
86-
</ActorDetailsSettingsProvider>
87-
</ActorContextProvider>
62+
{supportsConsole ? (
63+
<ActorWorkerContextProvider
64+
actorId={actorId}
65+
// notifyOnReconnect={features?.includes(
66+
// ActorFeature.InspectReconnectNotification,
67+
// )}
68+
>
69+
<ActorConsole actorId={actorId} />
70+
</ActorWorkerContextProvider>
71+
) : null}
72+
</div>
73+
</ActorDetailsSettingsProvider>
8874
);
8975
},
9076
);
@@ -234,7 +220,9 @@ export function ActorTabs({
234220
className="min-h-0 flex-1 mt-0 h-full"
235221
>
236222
<Suspense fallback={<ActorLogsTab.Skeleton />}>
237-
<ActorLogsTab actorId={actorId} />
223+
<GuardConnectableInspector actorId={actorId}>
224+
<ActorLogsTab actorId={actorId} />
225+
</GuardConnectableInspector>
238226
</Suspense>
239227
</TabsContent>
240228
) : null}
@@ -251,39 +239,49 @@ export function ActorTabs({
251239
value="connections"
252240
className="min-h-0 flex-1 mt-0"
253241
>
254-
<ActorConnectionsTab actorId={actorId} />
242+
<GuardConnectableInspector actorId={actorId}>
243+
<ActorConnectionsTab actorId={actorId} />
244+
</GuardConnectableInspector>
255245
</TabsContent>
256246
) : null}
257247
{supportsEvents ? (
258248
<TabsContent
259249
value="events"
260250
className="min-h-0 flex-1 mt-0"
261251
>
262-
<ActorEventsTab actorId={actorId} />
252+
<GuardConnectableInspector actorId={actorId}>
253+
<ActorEventsTab actorId={actorId} />
254+
</GuardConnectableInspector>
263255
</TabsContent>
264256
) : null}
265257
{supportsDatabase ? (
266258
<TabsContent
267259
value="database"
268260
className="min-h-0 min-w-0 flex-1 mt-0 h-full"
269261
>
270-
<ActorDatabaseTab actorId={actorId} />
262+
<GuardConnectableInspector actorId={actorId}>
263+
<ActorDatabaseTab actorId={actorId} />
264+
</GuardConnectableInspector>
271265
</TabsContent>
272266
) : null}
273267
{supportsState ? (
274268
<TabsContent
275269
value="state"
276270
className="min-h-0 flex-1 mt-0"
277271
>
278-
<ActorStateTab actorId={actorId} />
272+
<GuardConnectableInspector actorId={actorId}>
273+
<ActorStateTab actorId={actorId} />
274+
</GuardConnectableInspector>
279275
</TabsContent>
280276
) : null}
281277
{supportsMetrics ? (
282278
<TabsContent
283279
value="metrics"
284280
className="min-h-0 flex-1 mt-0 h-full"
285281
>
286-
<ActorMetricsTab actorId={actorId} />
282+
<GuardConnectableInspector actorId={actorId}>
283+
<ActorMetricsTab actorId={actorId} />
284+
</GuardConnectableInspector>
287285
</TabsContent>
288286
) : null}
289287
</>
@@ -292,77 +290,3 @@ export function ActorTabs({
292290
</Tabs>
293291
);
294292
}
295-
296-
function ActorContextProvider(props: {
297-
actorId: ActorId;
298-
children: ReactNode;
299-
}) {
300-
return __APP_TYPE__ === "inspector" ? (
301-
<ActorInspectorProvider {...props} />
302-
) : (
303-
<ActorEngineProvider {...props} />
304-
);
305-
}
306-
307-
function ActorInspectorProvider({
308-
actorId,
309-
children,
310-
}: {
311-
actorId: ActorId;
312-
children: ReactNode;
313-
}) {
314-
const { data } = useSuspenseQuery(useManager().actorQueryOptions(actorId));
315-
const { credentials } = useInspectorCredentials();
316-
317-
if (!credentials?.url || !credentials?.token) {
318-
throw new Error("Missing inspector credentials");
319-
}
320-
321-
const actorContext = useMemo(() => {
322-
return createInspectorActorContext({
323-
...credentials,
324-
name: data.name || "",
325-
});
326-
}, [credentials, data.name]);
327-
328-
return <ActorProvider value={actorContext}>{children}</ActorProvider>;
329-
}
330-
331-
function ActorEngineProvider({
332-
actorId,
333-
children,
334-
}: {
335-
actorId: ActorId;
336-
children: ReactNode;
337-
}) {
338-
const { data: actor } = useSuspenseQuery(
339-
useManager().actorQueryOptions(actorId),
340-
);
341-
342-
const match = useMatch({
343-
from: "/_layout/ns/$namespace",
344-
});
345-
346-
if (!match.params.namespace || !actor.runner) {
347-
throw new Error("Actor is missing required fields");
348-
}
349-
350-
const { data: runners } = useSuspenseInfiniteQuery(
351-
runnersQueryOptions({
352-
namespace: match.params.namespace as NamespaceNameId,
353-
}),
354-
);
355-
356-
const runner = runners.find((runner) => runner.name === actor.runner);
357-
358-
if (!runner) {
359-
throw new Error("Runner not found");
360-
}
361-
362-
const actorContext = useMemo(() => {
363-
return createEngineActorContext({
364-
token: (runner.metadata?.inspectorToken as string) || "",
365-
});
366-
}, [runner.metadata?.inspectorToken]);
367-
return <ActorProvider value={actorContext}>{children}</ActorProvider>;
368-
}

0 commit comments

Comments
 (0)