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
3 changes: 1 addition & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@
"@radix-ui/react-tooltip": "^1.1.1",
"@radix-ui/react-visually-hidden": "^1.0.3",
"@rivet-gg/icons": "file:./vendor/rivet-icons.tgz",
"@rivetkit/actor": "file:./vendor/rivetkit-actor.tgz",
"@rivetkit/core": "file:./vendor/rivetkit-core.tgz",
"rivetkit": "*",
"@rivetkit/engine-api-full": "workspace:*",
"@sentry/react": "^8.26.0",
"@sentry/vite-plugin": "^2.22.2",
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ declare module "@tanstack/react-router" {
}
}

declare module "@tanstack/react-query" {
interface Register {
queryMeta: {
mightRequireAuth?: boolean;
};
}
}

export const router = createRouter({
basepath: import.meta.env.BASE_URL,
routeTree,
Expand Down
82 changes: 82 additions & 0 deletions frontend/src/app/dialogs/provide-engine-credentials-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as EngineCredentialsForm from "@/app/forms/engine-credentials-form";
import {
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
Flex,
getConfig,
ls,
toast,
} from "@/components";
import type { DialogContentProps } from "@/components/actors/hooks";
import { queryClient } from "@/queries/global";
import { createClient } from "@/queries/manager-engine";

interface ProvideEngineCredentialsDialogContentProps
extends DialogContentProps {}

export default function ProvideEngineCredentialsDialogContent({
onClose,
}: ProvideEngineCredentialsDialogContentProps) {
return (
<EngineCredentialsForm.Form
defaultValues={{ token: "" }}
errors={
ls.engineCredentials.get(getConfig().apiUrl)
? { token: { message: "Invalid token.", type: "manual" } }
: {}
}
onSubmit={async (values, form) => {
const client = createClient({
token: values.token,
});

try {
await client.namespaces.list();

ls.engineCredentials.set(getConfig().apiUrl, values.token);

toast.success(
"Successfully authenticated with Rivet Engine",
);

await queryClient.refetchQueries();

onClose?.();
} catch (e) {
if (e && typeof e === "object" && "statusCode" in e) {
if (e.statusCode === 403) {
form.setError("token", {
message: "Invalid token.",
});
return;
}
}

form.setError("token", {
message: "Failed to connect. Please try again.",
});
return;
}
}}
>
<DialogHeader>
<DialogTitle>Missing Rivet Engine credentials</DialogTitle>
<DialogDescription>
It looks like the instance of Rivet Engine that you're
connected to requires additional credentials, please provide
them below.
</DialogDescription>
</DialogHeader>
<Flex gap="4" direction="col">
<EngineCredentialsForm.Token />
</Flex>
<DialogFooter>
<EngineCredentialsForm.Submit type="submit" allowPristine>
Save
</EngineCredentialsForm.Submit>
</DialogFooter>
</EngineCredentialsForm.Form>
);
}
47 changes: 47 additions & 0 deletions frontend/src/app/forms/engine-credentials-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { type UseFormReturn, useFormContext } from "react-hook-form";
import z from "zod";
import {
createSchemaForm,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
Input,
} from "@/components";

export const formSchema = z.object({
token: z.string().nonempty("Token is required"),
});

export type FormValues = z.infer<typeof formSchema>;
export type SubmitHandler = (
values: FormValues,
form: UseFormReturn<FormValues>,
) => Promise<void>;

const { Form, Submit, SetValue } = createSchemaForm(formSchema);
export { Form, Submit, SetValue };

export const Token = ({ className }: { className?: string }) => {
const { control } = useFormContext<FormValues>();
return (
<FormField
control={control}
name="token"
render={({ field }) => (
<FormItem className={className}>
<FormLabel className="col-span-1">Token</FormLabel>
<FormControl className="row-start-2">
<Input
placeholder="Enter a token..."
type="password"
{...field}
/>
</FormControl>
<FormMessage className="col-span-1" />
</FormItem>
)}
/>
);
};
21 changes: 13 additions & 8 deletions frontend/src/app/use-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { createDialogHook, useDialog } from "@/components/actors";
import {
useDialog as baseUseDialog,
createDialogHook,
} from "@/components/actors";

const d = useDialog as typeof useDialog &
Record<string, ReturnType<typeof createDialogHook>>;
d.CreateNamespace = createDialogHook(
import("@/app/dialogs/create-namespace-dialog"),
);

export { d as useDialog };
export const useDialog = {
...baseUseDialog,
CreateNamespace: createDialogHook(
import("@/app/dialogs/create-namespace-dialog"),
),
ProvideEngineCredentials: createDialogHook(
import("@/app/dialogs/provide-engine-credentials-dialog"),
),
};
2 changes: 1 addition & 1 deletion frontend/src/components/actors/actor-context.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export {
createActorInspectorClient,
createManagerInspectorClient,
} from "@rivetkit/core/inspector";
} from "rivetkit/inspector";
2 changes: 1 addition & 1 deletion frontend/src/components/actors/actor-events-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import {
faUnlink,
Icon,
} from "@rivet-gg/icons";
import type { RecordedRealtimeEvent } from "@rivetkit/core/inspector";
import { useQuery } from "@tanstack/react-query";
import { format } from "date-fns";
import { type PropsWithChildren, useEffect, useRef } from "react";
import type { RecordedRealtimeEvent } from "rivetkit/inspector";
import { Badge } from "../ui/badge";
import { useActor } from "./actor-queries-context";
import { ActorObjectInspector } from "./console/actor-inspector";
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/actors/actor-queries-context.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createActorInspectorClient } from "@rivetkit/core/inspector";
import { queryOptions } from "@tanstack/react-query";
import { createContext, useContext } from "react";
import { createActorInspectorClient } from "rivetkit/inspector";
import type { ActorId } from "./queries";

type RequestOptions = Parameters<typeof createActorInspectorClient>[1];
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/actors/database/database-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
faLink,
Icon,
} from "@rivet-gg/icons";
import type { Column, Columns, ForeignKeys } from "@rivetkit/core/inspector";
import {
createColumnHelper,
// SortingState,
Expand All @@ -19,6 +18,7 @@ import {
useReactTable as useTable,
} from "@tanstack/react-table";
import { Fragment, useMemo, useState } from "react";
import type { Column, Columns, ForeignKeys } from "rivetkit/inspector";
import {
Badge,
Button,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from "@/queries/manager-engine";
import { DiscreteCopyButton } from "../copy-area";
import { getConfig } from "../lib/config";
import { ls } from "../lib/utils";
import { Button } from "../ui/button";
import { useFiltersValue } from "./actor-filters-context";
import { ActorProvider } from "./actor-queries-context";
Expand Down Expand Up @@ -157,6 +158,7 @@ function useActorEngineContext({ actorId }: { actorId: ActorId }) {
return createInspectorActorContext({
url: getConfig().apiUrl,
token: (runner?.metadata?.inspectorToken as string) || "",
engineToken: ls.engineCredentials.get(getConfig().apiUrl) || "",
});
}, [runner?.metadata?.inspectorToken]);

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/actors/manager-context.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { CreateActor as InspectorCreateActor } from "@rivetkit/core/inspector";
import {
infiniteQueryOptions,
type MutationOptions,
type QueryClient,
queryOptions,
} from "@tanstack/react-query";
import { createContext, useContext } from "react";
import type { CreateActor as InspectorCreateActor } from "rivetkit/inspector";
import { z } from "zod";
import { queryClient } from "@/queries/global";
import {
Expand Down
6 changes: 1 addition & 5 deletions frontend/src/components/actors/queries/actor.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { fetchEventSource } from "@microsoft/fetch-event-source";
import type {
ActorId,
Patch,
RecordedRealtimeEvent,
} from "@rivetkit/core/inspector";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { applyPatch, compare } from "fast-json-patch";
import { useCallback, useEffect, useMemo } from "react";
import type { ActorId, Patch, RecordedRealtimeEvent } from "rivetkit/inspector";
import { useActor } from "../actor-queries-context";

export const useActorClearEventsMutation = (
Expand Down
9 changes: 4 additions & 5 deletions frontend/src/components/actors/queries/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type { Actor as InspectorActor } from "@rivetkit/core/inspector";
import type { NamespaceNameId } from "@/queries/manager-engine";
import type { Actor as InspectorActor } from "rivetkit/inspector";

export type { ActorLogEntry } from "@rivetkit/core/inspector";
export { ActorFeature } from "@rivetkit/core/inspector";
export type { ActorLogEntry } from "rivetkit/inspector";
export { ActorFeature } from "rivetkit/inspector";

import type { ActorId } from "@rivetkit/core/inspector";
import type { ActorId } from "rivetkit/inspector";

export type { ActorId };

Expand Down
9 changes: 5 additions & 4 deletions frontend/src/components/actors/worker/actor-repl.worker.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { createClient } from "@rivetkit/actor/client";
import { fromJs } from "esast-util-from-js";
import { toJs } from "estree-util-to-js";
import { createClient } from "rivetkit/client";
import {
createHighlighterCore,
createOnigurumaEngine,
type HighlighterCore,
} from "shiki";
import { getConfig } from "@/components";
import { createEngineActorContext } from "@/queries/actor-engine";
import {
type InitMessage,
MessageSchema,
Expand Down Expand Up @@ -183,6 +181,9 @@ function respond(msg: Response) {
async function callAction({ name, args }: { name: string; args: unknown[] }) {
if (!init) throw new Error("Actor not initialized");

const client = createClient(init.endpoint).getForId(init.name, init.id);
const client = createClient({
endpoint: init.endpoint,
token: init.engineToken,
}).getForId(init.name, init.id);
return await client.action({ name, args });
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export class ActorWorkerContainer {
rpcs: string[];
endpoint?: string;
name?: string;
engineToken?: string;
} | null = null;

#listeners: (() => void)[] = [];
Expand All @@ -56,16 +57,18 @@ export class ActorWorkerContainer {
rpcs = [],
endpoint,
name,
engineToken,
}: {
actorId: string;
signal: AbortSignal;
rpcs?: string[];
endpoint?: string;
name?: string;
engineToken?: string;
}) {
this.terminate();

this.#meta = { actorId, rpcs, endpoint, name };
this.#meta = { actorId, rpcs, endpoint, name, engineToken };
this.#state.status = { type: "pending" };
this.#update();
try {
Expand Down Expand Up @@ -142,6 +145,7 @@ export class ActorWorkerContainer {
id: this.#meta?.actorId ?? "",
endpoint: this.#meta?.endpoint ?? "",
name: this.#meta?.name ?? "",
engineToken: this.#meta?.engineToken ?? "",
} satisfies InitMessage);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
useState,
useSyncExternalStore,
} from "react";
import { assertNonNullable } from "../../lib/utils";
import { getConfig } from "@/components/lib/config";
import { assertNonNullable, ls } from "../../lib/utils";
import { useActor } from "../actor-queries-context";
import { useManager } from "../manager-context";
import { ActorFeature, type ActorId } from "../queries";
Expand Down Expand Up @@ -69,6 +70,7 @@ export const ActorWorkerContextProvider = ({
name,
signal: ctrl.signal,
rpcs,
engineToken: ls.engineCredentials.get(getConfig().apiUrl) || "",
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const CodeMessageSchema = z.object({
const InitMessageSchema = z.object({
type: z.literal("init"),
rpcs: z.array(z.string()).optional(),
engineToken: z.string().optional(),
endpoint: z.string(),
name: z.string(),
id: z.string(),
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/lib/create-schema-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
type FieldPath,
type FieldValues,
type PathValue,
type UseFormProps,
type UseFormReturn,
useForm,
useFormContext,
Expand All @@ -18,6 +19,7 @@ interface FormProps<FormValues extends FieldValues>
extends Omit<ComponentProps<"form">, "onSubmit"> {
onSubmit: SubmitHandler<FormValues>;
defaultValues: DefaultValues<FormValues>;
errors?: UseFormProps<FormValues>["errors"];
values?: FormValues;
children: ReactNode;
}
Expand All @@ -36,13 +38,15 @@ export const createSchemaForm = <Schema extends z.ZodSchema>(
values,
children,
onSubmit,
errors,
...props
}: FormProps<z.TypeOf<Schema>>) => {
const form = useForm<z.TypeOf<Schema>>({
reValidateMode: "onSubmit",
resolver: zodResolver(schema),
defaultValues,
values,
errors,
});
return (
<Form {...form}>
Expand Down
Loading
Loading