mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-20 20:48:11 -05:00
Compare commits
6 Commits
testing-cl
...
fix/proxy-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6c539e48e | ||
|
|
8bbd2a8125 | ||
|
|
5b4ce4c93f | ||
|
|
1210e8ef04 | ||
|
|
dc84b777c0 | ||
|
|
bed812d7a5 |
@@ -1,23 +1,5 @@
|
|||||||
import { FC, useEffect, useMemo, useState } from "react";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import SchemaTooltip from "@/components/SchemaTooltip";
|
import SchemaTooltip from "@/components/SchemaTooltip";
|
||||||
import useCredentials from "@/hooks/useCredentials";
|
import { Button } from "@/components/ui/button";
|
||||||
import { NotionLogoIcon } from "@radix-ui/react-icons";
|
|
||||||
import {
|
|
||||||
FaDiscord,
|
|
||||||
FaGithub,
|
|
||||||
FaTwitter,
|
|
||||||
FaGoogle,
|
|
||||||
FaMedium,
|
|
||||||
FaKey,
|
|
||||||
FaHubspot,
|
|
||||||
} from "react-icons/fa";
|
|
||||||
import {
|
|
||||||
BlockIOCredentialsSubSchema,
|
|
||||||
CredentialsMetaInput,
|
|
||||||
CredentialsProviderName,
|
|
||||||
} from "@/lib/autogpt-server-api/types";
|
|
||||||
import { IconKey, IconKeyPlus, IconUserPlus } from "@/components/ui/icons";
|
import { IconKey, IconKeyPlus, IconUserPlus } from "@/components/ui/icons";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
@@ -27,12 +9,30 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import useCredentials from "@/hooks/useCredentials";
|
||||||
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
|
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
|
||||||
|
import {
|
||||||
|
BlockIOCredentialsSubSchema,
|
||||||
|
CredentialsMetaInput,
|
||||||
|
CredentialsProviderName,
|
||||||
|
} from "@/lib/autogpt-server-api/types";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { getHostFromUrl } from "@/lib/utils/url";
|
||||||
|
import { NotionLogoIcon } from "@radix-ui/react-icons";
|
||||||
|
import { FC, useEffect, useMemo, useState } from "react";
|
||||||
|
import {
|
||||||
|
FaDiscord,
|
||||||
|
FaGithub,
|
||||||
|
FaGoogle,
|
||||||
|
FaHubspot,
|
||||||
|
FaKey,
|
||||||
|
FaMedium,
|
||||||
|
FaTwitter,
|
||||||
|
} from "react-icons/fa";
|
||||||
import { APIKeyCredentialsModal } from "./api-key-credentials-modal";
|
import { APIKeyCredentialsModal } from "./api-key-credentials-modal";
|
||||||
import { UserPasswordCredentialsModal } from "./user-password-credentials-modal";
|
|
||||||
import { HostScopedCredentialsModal } from "./host-scoped-credentials-modal";
|
import { HostScopedCredentialsModal } from "./host-scoped-credentials-modal";
|
||||||
import { OAuth2FlowWaitingModal } from "./oauth2-flow-waiting-modal";
|
import { OAuth2FlowWaitingModal } from "./oauth2-flow-waiting-modal";
|
||||||
import { getHostFromUrl } from "@/lib/utils/url";
|
import { UserPasswordCredentialsModal } from "./user-password-credentials-modal";
|
||||||
|
|
||||||
const fallbackIcon = FaKey;
|
const fallbackIcon = FaKey;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { getWebSocketToken } from "@/lib/supabase/actions";
|
|||||||
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
|
import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase";
|
||||||
import { createBrowserClient } from "@supabase/ssr";
|
import { createBrowserClient } from "@supabase/ssr";
|
||||||
import type { SupabaseClient } from "@supabase/supabase-js";
|
import type { SupabaseClient } from "@supabase/supabase-js";
|
||||||
import { proxyApiRequest, proxyFileUpload } from "./proxy-action";
|
|
||||||
import type {
|
import type {
|
||||||
AddUserCreditsResponse,
|
AddUserCreditsResponse,
|
||||||
AnalyticsDetails,
|
AnalyticsDetails,
|
||||||
@@ -27,6 +26,7 @@ import type {
|
|||||||
GraphID,
|
GraphID,
|
||||||
GraphMeta,
|
GraphMeta,
|
||||||
GraphUpdateable,
|
GraphUpdateable,
|
||||||
|
HostScopedCredentials,
|
||||||
LibraryAgent,
|
LibraryAgent,
|
||||||
LibraryAgentID,
|
LibraryAgentID,
|
||||||
LibraryAgentPreset,
|
LibraryAgentPreset,
|
||||||
@@ -62,7 +62,6 @@ import type {
|
|||||||
User,
|
User,
|
||||||
UserOnboarding,
|
UserOnboarding,
|
||||||
UserPasswordCredentials,
|
UserPasswordCredentials,
|
||||||
HostScopedCredentials,
|
|
||||||
UsersBalanceHistoryResponse,
|
UsersBalanceHistoryResponse,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
@@ -521,8 +520,6 @@ export default class BackendAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uploadStoreSubmissionMedia(file: File): Promise<string> {
|
uploadStoreSubmissionMedia(file: File): Promise<string> {
|
||||||
const formData = new FormData();
|
|
||||||
formData.append("file", file);
|
|
||||||
return this._uploadFile("/store/submissions/media", file);
|
return this._uploadFile("/store/submissions/media", file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -813,8 +810,48 @@ export default class BackendAPI {
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file", file);
|
formData.append("file", file);
|
||||||
|
|
||||||
// Use proxy server action for secure file upload
|
if (isClient) {
|
||||||
return await proxyFileUpload(path, formData, this.baseUrl);
|
return this._makeClientFileUpload(path, formData);
|
||||||
|
} else {
|
||||||
|
return this._makeServerFileUpload(path, formData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _makeClientFileUpload(
|
||||||
|
path: string,
|
||||||
|
formData: FormData,
|
||||||
|
): Promise<string> {
|
||||||
|
// Dynamic import is required even for client-only functions because helpers.ts
|
||||||
|
// has server-only imports (like getServerSupabase) at the top level. Static imports
|
||||||
|
// would bundle server-only code into the client bundle, causing runtime errors.
|
||||||
|
const { buildClientUrl, parseErrorResponse, handleFetchError } =
|
||||||
|
await import("./helpers");
|
||||||
|
|
||||||
|
const uploadUrl = buildClientUrl(path);
|
||||||
|
|
||||||
|
const response = await fetch(uploadUrl, {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await parseErrorResponse(response);
|
||||||
|
throw handleFetchError(response, errorData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _makeServerFileUpload(
|
||||||
|
path: string,
|
||||||
|
formData: FormData,
|
||||||
|
): Promise<string> {
|
||||||
|
const { makeAuthenticatedFileUpload, buildServerUrl } = await import(
|
||||||
|
"./helpers"
|
||||||
|
);
|
||||||
|
const url = buildServerUrl(path);
|
||||||
|
return await makeAuthenticatedFileUpload(url, formData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _request(
|
private async _request(
|
||||||
@@ -826,13 +863,62 @@ export default class BackendAPI {
|
|||||||
console.debug(`${method} ${path} payload:`, payload);
|
console.debug(`${method} ${path} payload:`, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always use proxy server action to not expose any auth tokens to the browser
|
if (isClient) {
|
||||||
return await proxyApiRequest({
|
return this._makeClientRequest(method, path, payload);
|
||||||
|
} else {
|
||||||
|
return this._makeServerRequest(method, path, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _makeClientRequest(
|
||||||
|
method: string,
|
||||||
|
path: string,
|
||||||
|
payload?: Record<string, any>,
|
||||||
|
) {
|
||||||
|
// Dynamic import is required even for client-only functions because helpers.ts
|
||||||
|
// has server-only imports (like getServerSupabase) at the top level. Static imports
|
||||||
|
// would bundle server-only code into the client bundle, causing runtime errors.
|
||||||
|
const {
|
||||||
|
buildClientUrl,
|
||||||
|
buildUrlWithQuery,
|
||||||
|
parseErrorResponse,
|
||||||
|
handleFetchError,
|
||||||
|
} = await import("./helpers");
|
||||||
|
|
||||||
|
const payloadAsQuery = ["GET", "DELETE"].includes(method);
|
||||||
|
let url = buildClientUrl(path);
|
||||||
|
|
||||||
|
if (payloadAsQuery && payload) {
|
||||||
|
url = buildUrlWithQuery(url, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
method,
|
method,
|
||||||
path,
|
headers: {
|
||||||
payload,
|
"Content-Type": "application/json",
|
||||||
baseUrl: this.baseUrl,
|
},
|
||||||
|
body: !payloadAsQuery && payload ? JSON.stringify(payload) : undefined,
|
||||||
|
credentials: "include",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await parseErrorResponse(response);
|
||||||
|
throw handleFetchError(response, errorData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _makeServerRequest(
|
||||||
|
method: string,
|
||||||
|
path: string,
|
||||||
|
payload?: Record<string, any>,
|
||||||
|
) {
|
||||||
|
const { makeAuthenticatedRequest, buildServerUrl } = await import(
|
||||||
|
"./helpers"
|
||||||
|
);
|
||||||
|
const url = buildServerUrl(path);
|
||||||
|
return await makeAuthenticatedRequest(method, url, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
|
|||||||
@@ -29,6 +29,42 @@ export function buildRequestUrl(
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function buildClientUrl(path: string): string {
|
||||||
|
return `/api/proxy/api${path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildServerUrl(path: string): string {
|
||||||
|
const baseUrl =
|
||||||
|
process.env.NEXT_PUBLIC_AGPT_SERVER_URL || "http://localhost:8006/api";
|
||||||
|
return `${baseUrl}${path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildUrlWithQuery(
|
||||||
|
url: string,
|
||||||
|
payload?: Record<string, any>,
|
||||||
|
): string {
|
||||||
|
if (!payload) return url;
|
||||||
|
|
||||||
|
const queryParams = new URLSearchParams(payload);
|
||||||
|
return `${url}?${queryParams.toString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleFetchError(response: Response, errorData: any): ApiError {
|
||||||
|
return new ApiError(
|
||||||
|
errorData?.error || "Request failed",
|
||||||
|
response.status,
|
||||||
|
errorData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function parseErrorResponse(response: Response): Promise<any> {
|
||||||
|
try {
|
||||||
|
return await response.json();
|
||||||
|
} catch {
|
||||||
|
return { error: response.statusText };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function getServerAuthToken(): Promise<string> {
|
export async function getServerAuthToken(): Promise<string> {
|
||||||
const supabase = await getServerSupabase();
|
const supabase = await getServerSupabase();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user