Files
Ivan Starkov 9348ce0af0 fix: Image upload via share link (#4083)
## Description

Use authToken token implicitly. Except queue cases like image upload and
patches.

## Steps for reproduction

Open any project with build or admin share link, upload image.

https://p-a82eaa2b-698e-4d3f-aa46-d4f3eee05fe0-dot-img-fix.development.webstudio.is/?authToken=a74ddf79-2638-47af-9dd1-e62f78187a8d&mode=edit

<img width="1025" alt="image"
src="https://github.com/user-attachments/assets/1de48271-f8c3-4df8-aa51-e61d74eb5c32">


## Code Review

- [ ] hi @kof, I need you to do
  - conceptual review (architecture, feature-correctness)
  - detailed review (read every line)
  - test it on preview

## Before requesting a review

- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")

## Before merging

- [ ] tested locally and on preview environment (preview dev login:
5de6)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env` file
2024-09-07 18:43:03 +03:00

134 lines
3.7 KiB
TypeScript

import type { AppRouter } from "~/services/trcp-router.server";
import { createTRPCProxyClient, httpBatchLink } from "@trpc/client";
import type {
AnyMutationProcedure,
AnyRouter,
inferRouterInputs,
inferRouterOutputs,
} from "@trpc/server";
import { createRecursiveProxy } from "@trpc/server/shared";
import { useMemo, useState } from "react";
import { fetch } from "~/shared/fetch.client";
export const nativeClient = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
fetch,
url: "/trpc",
}),
],
});
type Procedures<T> = T extends AnyRouter
? {
[Procedure in keyof T["_def"]["record"]]: T["_def"]["record"][Procedure] extends AnyMutationProcedure
? {
useMutation: () => {
send: (
input: inferRouterInputs<T>[Procedure],
onSuccess?: (data: inferRouterOutputs<T>[Procedure]) => void
) => void;
data?: inferRouterOutputs<T>[Procedure];
state: "idle" | "submitting";
error?: string | undefined;
};
}
: {
useQuery: () => {
load: (
input: inferRouterInputs<T>[Procedure],
onSuccess?: (data: inferRouterOutputs<T>[Procedure]) => void
) => void;
data?: inferRouterOutputs<T>[Procedure];
state: "idle" | "submitting";
error?: string | undefined;
};
};
}
: never;
type Client = Record<
string,
Record<
string,
{ query: (args: unknown) => unknown; mutate: (args: unknown) => unknown }
>
>;
export const trpcClient: {
[SubRoute in keyof AppRouter["_def"]["record"]]: Procedures<
AppRouter["_def"]["record"][SubRoute]
>;
} = createRecursiveProxy((options) => {
const path = [...options.path];
const hook = path.pop();
const [data, setData] = useState<unknown>(undefined);
const [state, setState] = useState<"idle" | "submitting">("idle");
const [error, setError] = useState<string | undefined>(undefined);
if (hook !== "useMutation" && hook !== "useQuery") {
throw new Error(`Invalid hook ${hook}`);
}
// const method = path.join(".");
if (path.length !== 2) {
throw new Error(`Invalid path ${path}`);
}
const [namespace, method] = path;
const submit = useMemo(() => {
let requestId = 0;
return async (input: never, onSuccess?: (data: unknown) => void) => {
requestId += 1;
const currentRequestId = requestId;
try {
setState("submitting");
// if (process.env.NODE_ENV !== "production") {
// await new Promise((resolve) => setTimeout(resolve, 1000));
// }
const result = await (nativeClient as unknown as Client)[namespace][
method
][hook === "useMutation" ? "mutate" : "query"](input);
// Newer request has been made, ignore this one
// In the future to support optimistic updates we can call onComplete with the requestId order
if (currentRequestId !== requestId) {
return;
}
setData(result);
setError(undefined);
onSuccess?.(result);
} catch (error) {
if (currentRequestId !== requestId) {
return;
}
console.error("TRPC ERROR", error);
if (error instanceof Error) {
setError(error.message);
return;
}
setError("Unknown error");
} finally {
if (currentRequestId === requestId) {
setState("idle");
}
}
};
}, [hook, namespace, method]);
return {
[hook === "useMutation" ? "send" : "load"]: submit,
data: data,
state,
error,
};
}) as never;