diff --git a/apps/proxy/src/routes/proxy.ts b/apps/proxy/src/routes/proxy.ts index fd2d18567..bdf9e815f 100644 --- a/apps/proxy/src/routes/proxy.ts +++ b/apps/proxy/src/routes/proxy.ts @@ -192,24 +192,33 @@ async function proxyHandler(c: Context) { console.log('[Proxy Data]', { cleanedTargetUrl, data }); // Insert into ClickHouse (non-blocking, fire-and-forget) - void insertResourceInvocation({ - id: randomUUID(), - resource_id: null, // TODO: lookup resourceId by URL if needed - status_code: clonedUpstreamResponse.status, - duration: fetchDuration, - status_text: clonedUpstreamResponse.statusText, - method, - url: targetUrl.toString(), - request_content_type: - clonedRequest.headers.get('content-type') ?? '', - response_content_type: - clonedUpstreamResponse.headers.get('content-type') ?? '', - response_headers: responseHeaders, - response_body: responseBody, - request_headers: requestHeaders, - request_body: requestBody, - created_at: new Date(), - }); + // Skip insertion if skip_tracking is enabled (i.e developer/test mode) + const skipTracking = c.req.query('skip_tracking') === 'true'; + if (!skipTracking) { + void insertResourceInvocation({ + id: randomUUID(), + resource_id: null, // TODO: lookup resourceId by URL if needed + status_code: clonedUpstreamResponse.status, + duration: fetchDuration, + status_text: clonedUpstreamResponse.statusText, + method, + url: targetUrl.toString(), + request_content_type: + clonedRequest.headers.get('content-type') ?? '', + response_content_type: + clonedUpstreamResponse.headers.get('content-type') ?? '', + response_headers: responseHeaders, + response_body: responseBody, + request_headers: requestHeaders, + request_body: requestBody, + created_at: new Date(), + }); + } else { + console.log('Proxy Skip Tracking', { + url: targetUrl.toString(), + reason: 'skip_tracking parameter enabled', + }); + } } } catch (error) { console.error('[Proxy After Error]', { diff --git a/apps/scan/src/app/_components/resource-fetch/chains/evm.tsx b/apps/scan/src/app/_components/resource-fetch/chains/evm.tsx index 7bade1743..d8eb89f6b 100644 --- a/apps/scan/src/app/_components/resource-fetch/chains/evm.tsx +++ b/apps/scan/src/app/_components/resource-fetch/chains/evm.tsx @@ -26,6 +26,7 @@ interface Props { options?: Omit>, 'mutationFn'>; isTool?: boolean; text?: string; + skipTracking?: boolean; } export const FetchEvm: React.FC = ({ @@ -37,6 +38,7 @@ export const FetchEvm: React.FC = ({ options, isTool = false, text, + skipTracking = false, }) => { const { data: walletClient, isLoading: isLoadingWalletClient } = useWalletClient(); @@ -49,6 +51,7 @@ export const FetchEvm: React.FC = ({ init: typeof requestInit === 'function' ? requestInit(chain) : requestInit, options, isTool, + skipTracking, }); const { data: balance, isLoading: isLoadingBalance } = useEvmTokenBalance({ diff --git a/apps/scan/src/app/_components/resource-fetch/chains/svm.tsx b/apps/scan/src/app/_components/resource-fetch/chains/svm.tsx index 6ecb8d814..25b826f80 100644 --- a/apps/scan/src/app/_components/resource-fetch/chains/svm.tsx +++ b/apps/scan/src/app/_components/resource-fetch/chains/svm.tsx @@ -27,6 +27,7 @@ interface Props { options?: Omit>, 'mutationFn'>; isTool?: boolean; text?: string; + skipTracking?: boolean; } export const FetchSvm: React.FC = ({ @@ -37,6 +38,7 @@ export const FetchSvm: React.FC = ({ options, isTool = false, text, + skipTracking = false, }) => { const { connectedWallet } = useSolanaWallet(); @@ -74,6 +76,7 @@ export const FetchSvm: React.FC = ({ options={options} isTool={isTool} text={text} + skipTracking={skipTracking} /> ); }; @@ -92,6 +95,7 @@ const FetchContent: React.FC = ({ options, isTool = false, text, + skipTracking = false, }) => { const { mutate: execute, isPending } = useSvmX402Fetch({ account, @@ -103,6 +107,7 @@ const FetchContent: React.FC = ({ : requestInit, options, isTool, + skipTracking, }); const { isInitialized } = useIsInitialized(); diff --git a/apps/scan/src/app/_components/resource-fetch/index.tsx b/apps/scan/src/app/_components/resource-fetch/index.tsx index 1354b8e69..92aa99690 100644 --- a/apps/scan/src/app/_components/resource-fetch/index.tsx +++ b/apps/scan/src/app/_components/resource-fetch/index.tsx @@ -18,6 +18,7 @@ interface Props { options?: Omit>, 'mutationFn'>; isTool?: boolean; text?: string; + skipTracking?: boolean; } export const ResourceFetch: React.FC = ({ @@ -29,6 +30,7 @@ export const ResourceFetch: React.FC = ({ options, isTool = false, text, + skipTracking = false, }) => { return (
= ({ options={options} isTool={isTool} text={text} + skipTracking={skipTracking} /> ) : ( = ({ options={options} isTool={isTool} text={text} + skipTracking={skipTracking} /> ) )} diff --git a/apps/scan/src/app/_components/resources/executor/form/index.tsx b/apps/scan/src/app/_components/resources/executor/form/index.tsx index 01a6c5561..273ebe4db 100644 --- a/apps/scan/src/app/_components/resources/executor/form/index.tsx +++ b/apps/scan/src/app/_components/resources/executor/form/index.tsx @@ -43,6 +43,7 @@ interface Props { maxAmountRequired: bigint; method: Methods; resource: string; + skipTracking?: boolean; } export function Form({ @@ -51,6 +52,7 @@ export function Form({ maxAmountRequired, method, resource, + skipTracking = false, }: Props) { const queryFields = useMemo( () => getFields(inputSchema.queryParams), @@ -230,6 +232,7 @@ export function Form({ onSuccess: data => setData(data), onError: error => setError(error), }} + skipTracking={skipTracking} /> {error && ( diff --git a/apps/scan/src/app/_components/resources/executor/index.tsx b/apps/scan/src/app/_components/resources/executor/index.tsx index e66501b5d..75feed11c 100644 --- a/apps/scan/src/app/_components/resources/executor/index.tsx +++ b/apps/scan/src/app/_components/resources/executor/index.tsx @@ -30,6 +30,7 @@ interface Props { hideOrigin?: boolean; defaultOpen?: boolean; isFlat?: boolean; + skipTracking?: boolean; } export const ResourceExecutor: React.FC = ({ @@ -40,6 +41,7 @@ export const ResourceExecutor: React.FC = ({ className, hideOrigin = false, isFlat = false, + skipTracking = false, }) => { const { data: resourceMetrics } = api.public.resources.getMetrics.useQuery( { @@ -95,6 +97,7 @@ export const ResourceExecutor: React.FC = ({ maxAmountRequired={maxAmountRequired} method={bazaarMethod} resource={resource.resource} + skipTracking={skipTracking} /> diff --git a/apps/scan/src/app/_hooks/x402/evm.ts b/apps/scan/src/app/_hooks/x402/evm.ts index bf4cbf890..ff1debd20 100644 --- a/apps/scan/src/app/_hooks/x402/evm.ts +++ b/apps/scan/src/app/_hooks/x402/evm.ts @@ -18,6 +18,7 @@ interface UseEvmX402FetchParams { init?: RequestInit; options?: Omit>, 'mutationFn'>; isTool?: boolean; + skipTracking?: boolean; } export const useEvmX402Fetch = ({ diff --git a/apps/scan/src/app/_hooks/x402/svm.ts b/apps/scan/src/app/_hooks/x402/svm.ts index 64ce0b794..e655e5636 100644 --- a/apps/scan/src/app/_hooks/x402/svm.ts +++ b/apps/scan/src/app/_hooks/x402/svm.ts @@ -16,6 +16,7 @@ interface UseSvmX402FetchParams { init?: RequestInit; options?: Omit>, 'mutationFn'>; isTool?: boolean; + skipTracking?: boolean; } export const useSvmX402Fetch = ({ diff --git a/apps/scan/src/app/_hooks/x402/use-fetch.ts b/apps/scan/src/app/_hooks/x402/use-fetch.ts index 5f74b9046..fc047698c 100644 --- a/apps/scan/src/app/_hooks/x402/use-fetch.ts +++ b/apps/scan/src/app/_hooks/x402/use-fetch.ts @@ -12,6 +12,7 @@ interface UseX402FetchParams { init?: RequestInit; options?: Omit>, 'mutationFn'>; isTool?: boolean; + skipTracking?: boolean; } export const useX402Fetch = ({ @@ -21,11 +22,15 @@ export const useX402Fetch = ({ init, options, isTool = false, + skipTracking = false, }: UseX402FetchParams) => { return useMutation({ mutationFn: async () => { const fetchWithPayment = wrapperFn( - isTool ? fetch : fetchWithProxy, + isTool + ? fetch + : (url: string | URL | Request, init?: RequestInit) => + fetchWithProxy(url, init, { skipTracking }), value ); const response = await fetchWithPayment(targetUrl, init); diff --git a/apps/scan/src/app/developer/_components/form.tsx b/apps/scan/src/app/developer/_components/form.tsx index 951b96452..95ae6e1ce 100644 --- a/apps/scan/src/app/developer/_components/form.tsx +++ b/apps/scan/src/app/developer/_components/form.tsx @@ -407,6 +407,7 @@ export const TestEndpointForm = () => { } hideOrigin isFlat + skipTracking={true} /> ))} diff --git a/apps/scan/src/lib/x402/proxy-fetch.ts b/apps/scan/src/lib/x402/proxy-fetch.ts index 4325fe65c..fe2f72bb7 100644 --- a/apps/scan/src/lib/x402/proxy-fetch.ts +++ b/apps/scan/src/lib/x402/proxy-fetch.ts @@ -4,7 +4,8 @@ const PROXY_ENDPOINT = '/api/proxy' as const; export const fetchWithProxy = async ( input: URL | RequestInfo, - requestInit?: RequestInit + requestInit?: RequestInit, + options?: { skipTracking?: boolean } ) => { let url: string; if (input instanceof Request) { @@ -15,6 +16,9 @@ export const fetchWithProxy = async ( const proxyUrl = new URL(PROXY_ENDPOINT, env.NEXT_PUBLIC_PROXY_URL); proxyUrl.searchParams.set('url', encodeURIComponent(url)); proxyUrl.searchParams.set('share_data', 'true'); + if (options?.skipTracking) { + proxyUrl.searchParams.set('skip_tracking', 'true'); + } const { method = 'GET', ...restInit } = requestInit ?? {}; const normalizedMethod = method.toString().toUpperCase(); diff --git a/apps/scan/src/trpc/routers/developer.ts b/apps/scan/src/trpc/routers/developer.ts index 4eac0e53f..7ae738479 100644 --- a/apps/scan/src/trpc/routers/developer.ts +++ b/apps/scan/src/trpc/routers/developer.ts @@ -4,6 +4,7 @@ import { createTRPCRouter, publicProcedure } from '../trpc'; import { getOriginFromUrl } from '@/lib/url'; import { parseX402Response } from '@/lib/x402/schema'; +import { fetchWithProxy } from '@/lib/x402/proxy-fetch'; import { scrapeOriginData } from '@/services/scraper'; export const developerRouter = createTRPCRouter({ @@ -56,15 +57,19 @@ export const developerRouter = createTRPCRouter({ .query(async ({ input }) => { const { method, url, headers = {} } = input; - const response = await fetch(url, { - method, - headers: - method === 'POST' - ? { ...headers, 'Content-Type': 'application/json' } - : headers, - body: method === 'POST' ? '{}' : undefined, - redirect: 'follow', - }); + const response = await fetchWithProxy( + url, + { + method, + headers: + method === 'POST' + ? { ...headers, 'Content-Type': 'application/json' } + : headers, + body: method === 'POST' ? '{}' : undefined, + redirect: 'follow', + }, + { skipTracking: true } + ); const text = await response.text(); let body: unknown = null;