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
25 changes: 16 additions & 9 deletions frontend/src/components/actors/actor-filters-context.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import { faHashtag, faKey } from "@rivet-gg/icons";
import { useQuery } from "@tanstack/react-query";
import { useSearch } from "@tanstack/react-router";
import { CommandGroup, CommandItem } from "cmdk";
import { createContext, useContext } from "react";
import { cn } from "../lib/utils";
import { Checkbox } from "../ui/checkbox";
import {
createFiltersPicker,
createFiltersRemover,
createFiltersSchema,
type FilterDefinitions,
FilterOp,
type OptionsProviderProps,
type PickFiltersOptions,
} from "../ui/filters";
import { ActorRegion } from "./actor-region";
import { ActorStatus } from "./actor-status";
import { useManager } from "./manager-context";
import type { ActorStatus as ActorStatusType } from "./queries";

export const ACTORS_FILTERS_DEFINITIONS = {
id: {
Expand Down Expand Up @@ -48,6 +40,13 @@ export const ACTORS_FILTERS_DEFINITIONS = {
category: "display",
ephemeral: true,
},
wakeOnSelect: {
type: "boolean",
label: "Auto-wake Actors on select",
category: "display",
ephemeral: true,
defaultValue: ["1"],
},
// tags: {
// type: "select",
// label: "Tags",
Expand Down Expand Up @@ -143,3 +142,11 @@ export const useFilters = (
select: (state) => fn(pick(state)),
});
};

export function useFiltersValue(opts: PickFiltersOptions = {}) {
const { pick } = useActorsFilters();
return useSearch({
from: "/_layout",
select: (state) => pick(state, opts),
});
}
38 changes: 38 additions & 0 deletions frontend/src/components/actors/actor-queries-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,44 @@ export const defaultActorContext = {
},
};
},

actorWakeUpMutationOptions(actorId: ActorId) {
return {
mutationKey: ["actor", actorId, "wake-up"],
mutationFn: async () => {
const client = this.createActorInspector(actorId);
try {
await client.ping.$get();
return true;
} catch {
return false;
}
},
};
},

actorAutoWakeUpQueryOptions(
actorId: ActorId,
{ enabled }: { enabled?: boolean } = {},
) {
return queryOptions({
enabled,
refetchInterval: 1000,
staleTime: 0,
gcTime: 0,
queryKey: ["actor", actorId, "auto-wake-up"],
queryFn: async ({ queryKey: [, actorId] }) => {
const client = this.createActorInspector(actorId);
try {
await client.ping.$get();
return true;
} catch {
return false;
}
},
retry: false,
});
},
};

export type ActorContext = typeof defaultActorContext;
Expand Down
15 changes: 2 additions & 13 deletions frontend/src/components/actors/actor-state-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,21 @@ import { Button } from "../ui/button";
import { ActorEditableState } from "./actor-editable-state";
import { useActor } from "./actor-queries-context";
import { useActorsView } from "./actors-view-context-provider";
import { useManager } from "./manager-context";
import type { ActorId } from "./queries";

interface ActorStateTabProps {
actorId: ActorId;
}

export function ActorStateTab({ actorId }: ActorStateTabProps) {
const { data: destroyedAt } = useQuery(
useManager().actorDestroyedAtQueryOptions(actorId),
);

const { links } = useActorsView();

const actorQueries = useActor();
const {
data: state,
isError,
isLoading,
} = useQuery(
actorQueries.actorStateQueryOptions(actorId, { enabled: !destroyedAt }),
);

if (destroyedAt) {
return <Info>State Preview is unavailable for inactive Actors.</Info>;
}
} = useQuery(actorQueries.actorStateQueryOptions(actorId));

if (isError) {
return (
Expand Down Expand Up @@ -69,7 +58,7 @@ export function ActorStateTab({ actorId }: ActorStateTabProps) {

export function Info({ children }: PropsWithChildren) {
return (
<div className="flex-1 flex flex-col gap-2 items-center justify-center h-full text-center">
<div className="flex-1 flex flex-col gap-2 items-center justify-center h-full text-center max-w-md mx-auto">
{children}
</div>
);
Expand Down
162 changes: 43 additions & 119 deletions frontend/src/components/actors/actors-actor-details.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { faQuestionSquare, Icon } from "@rivet-gg/icons";
import {
useQuery,
useSuspenseInfiniteQuery,
useSuspenseQuery,
} from "@tanstack/react-query";
import { useMatch } from "@tanstack/react-router";
import { memo, type ReactNode, Suspense, useMemo } from "react";
import { useInspectorCredentials } from "@/app/credentials-context";
import { useQuery } from "@tanstack/react-query";
import { memo, type ReactNode, Suspense } from "react";
import {
cn,
Flex,
Expand All @@ -15,26 +9,20 @@ import {
TabsList,
TabsTrigger,
} from "@/components";
import { createEngineActorContext } from "@/queries/actor-engine";
import { createInspectorActorContext } from "@/queries/actor-inspector";
import {
type NamespaceNameId,
runnersQueryOptions,
} from "@/queries/manager-engine";
import { ActorConfigTab } from "./actor-config-tab";
import { ActorConnectionsTab } from "./actor-connections-tab";
import { ActorDatabaseTab } from "./actor-db-tab";
import { ActorDetailsSettingsProvider } from "./actor-details-settings";
import { ActorEventsTab } from "./actor-events-tab";
import { ActorLogsTab } from "./actor-logs-tab";
import { ActorMetricsTab } from "./actor-metrics-tab";
import { ActorProvider } from "./actor-queries-context";
import { ActorStateTab } from "./actor-state-tab";
import { QueriedActorStatus } from "./actor-status";
import { ActorStopButton } from "./actor-stop-button";
import { ActorsSidebarToggleButton } from "./actors-sidebar-toggle-button";
import { useActorsView } from "./actors-view-context-provider";
import { ActorConsole } from "./console/actor-console";
import { GuardConnectableInspector } from "./guard-connectable-inspector";
import { useManager } from "./manager-context";
import { ActorFeature, type ActorId } from "./queries";
import { ActorWorkerContextProvider } from "./worker/actor-worker-context";
Expand All @@ -57,34 +45,32 @@ export const ActorsActorDetails = memo(
useManager().actorFeaturesQueryOptions(actorId),
);

const supportsConsole = features?.includes(ActorFeature.Console);
const supportsConsole = features.includes(ActorFeature.Console);

return (
<ActorContextProvider actorId={actorId}>
<ActorDetailsSettingsProvider>
<ActorWorkerContextProvider
<ActorDetailsSettingsProvider>
<div className="flex flex-col h-full flex-1">
<ActorTabs
features={features}
actorId={actorId}
// notifyOnReconnect={features?.includes(
// ActorFeature.InspectReconnectNotification,
// )}
>
<div className="flex flex-col h-full flex-1">
<ActorTabs
features={features}
actorId={actorId}
tab={tab}
onTabChange={onTabChange}
// onExportLogs={onExportLogs}
// isExportingLogs={isExportingLogs}
/>
tab={tab}
onTabChange={onTabChange}
// onExportLogs={onExportLogs}
// isExportingLogs={isExportingLogs}
/>

{supportsConsole ? (
<ActorConsole actorId={actorId} />
) : null}
</div>
</ActorWorkerContextProvider>
</ActorDetailsSettingsProvider>
</ActorContextProvider>
{supportsConsole ? (
<ActorWorkerContextProvider
actorId={actorId}
// notifyOnReconnect={features?.includes(
// ActorFeature.InspectReconnectNotification,
// )}
>
<ActorConsole actorId={actorId} />
</ActorWorkerContextProvider>
) : null}
</div>
</ActorDetailsSettingsProvider>
);
},
);
Expand Down Expand Up @@ -234,7 +220,9 @@ export function ActorTabs({
className="min-h-0 flex-1 mt-0 h-full"
>
<Suspense fallback={<ActorLogsTab.Skeleton />}>
<ActorLogsTab actorId={actorId} />
<GuardConnectableInspector actorId={actorId}>
<ActorLogsTab actorId={actorId} />
</GuardConnectableInspector>
</Suspense>
</TabsContent>
) : null}
Expand All @@ -251,39 +239,49 @@ export function ActorTabs({
value="connections"
className="min-h-0 flex-1 mt-0"
>
<ActorConnectionsTab actorId={actorId} />
<GuardConnectableInspector actorId={actorId}>
<ActorConnectionsTab actorId={actorId} />
</GuardConnectableInspector>
</TabsContent>
) : null}
{supportsEvents ? (
<TabsContent
value="events"
className="min-h-0 flex-1 mt-0"
>
<ActorEventsTab actorId={actorId} />
<GuardConnectableInspector actorId={actorId}>
<ActorEventsTab actorId={actorId} />
</GuardConnectableInspector>
</TabsContent>
) : null}
{supportsDatabase ? (
<TabsContent
value="database"
className="min-h-0 min-w-0 flex-1 mt-0 h-full"
>
<ActorDatabaseTab actorId={actorId} />
<GuardConnectableInspector actorId={actorId}>
<ActorDatabaseTab actorId={actorId} />
</GuardConnectableInspector>
</TabsContent>
) : null}
{supportsState ? (
<TabsContent
value="state"
className="min-h-0 flex-1 mt-0"
>
<ActorStateTab actorId={actorId} />
<GuardConnectableInspector actorId={actorId}>
<ActorStateTab actorId={actorId} />
</GuardConnectableInspector>
</TabsContent>
) : null}
{supportsMetrics ? (
<TabsContent
value="metrics"
className="min-h-0 flex-1 mt-0 h-full"
>
<ActorMetricsTab actorId={actorId} />
<GuardConnectableInspector actorId={actorId}>
<ActorMetricsTab actorId={actorId} />
</GuardConnectableInspector>
</TabsContent>
) : null}
</>
Expand All @@ -292,77 +290,3 @@ export function ActorTabs({
</Tabs>
);
}

function ActorContextProvider(props: {
actorId: ActorId;
children: ReactNode;
}) {
return __APP_TYPE__ === "inspector" ? (
<ActorInspectorProvider {...props} />
) : (
<ActorEngineProvider {...props} />
);
}

function ActorInspectorProvider({
actorId,
children,
}: {
actorId: ActorId;
children: ReactNode;
}) {
const { data } = useSuspenseQuery(useManager().actorQueryOptions(actorId));
const { credentials } = useInspectorCredentials();

if (!credentials?.url || !credentials?.token) {
throw new Error("Missing inspector credentials");
}

const actorContext = useMemo(() => {
return createInspectorActorContext({
...credentials,
name: data.name || "",
});
}, [credentials, data.name]);

return <ActorProvider value={actorContext}>{children}</ActorProvider>;
}

function ActorEngineProvider({
actorId,
children,
}: {
actorId: ActorId;
children: ReactNode;
}) {
const { data: actor } = useSuspenseQuery(
useManager().actorQueryOptions(actorId),
);

const match = useMatch({
from: "/_layout/ns/$namespace",
});

if (!match.params.namespace || !actor.runner) {
throw new Error("Actor is missing required fields");
}

const { data: runners } = useSuspenseInfiniteQuery(
runnersQueryOptions({
namespace: match.params.namespace as NamespaceNameId,
}),
);

const runner = runners.find((runner) => runner.name === actor.runner);

if (!runner) {
throw new Error("Runner not found");
}

const actorContext = useMemo(() => {
return createEngineActorContext({
token: (runner.metadata?.inspectorToken as string) || "",
});
}, [runner.metadata?.inspectorToken]);
return <ActorProvider value={actorContext}>{children}</ActorProvider>;
}
Loading
Loading