mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-19 18:00:15 +01:00
* ✨feat: OAuth for Actions * WIP: PoC flow state manager * refactor: Add identifier field to token model from action schema * chore: fix potential file type issues * ci: fix type issue with action metadata auth * fix: ensure FlowManagerOptions has a default ttl value * WIP: OAUTH actions * WIP: first pass OAuth Action * fix: standardize identifier usage in OAuth flow handling * fix: update token retrieval to include userId in query and use correct identifier * refacotr: update token retrieval to use userId for OAuth token query * feat: Tool Call Auth styling * fix: streamline token creation and add type field to token schema * refactor: cleanup OAuth flow by encrypting client credentials and ensuring oauth operations only run under condition * refactor: use encrypted credentials in OAuth callback * fix: update Token collection indexes to use expiresAt TTL index and not createdAt legacy index * refactor: enhance Token index cleanup by improving logging and removing redundant index creation logic * refactor: remove unused OAuth login route and related logic for improved clarity * refactor: replace fetch with axios for OAuth token exchange and improve error handling * refactor: better UX after authentication before oauth tool execution * refactor: implement cleanup handlers for FlowStateManager intervals to enhance resource management * refactor: encrypt OAuth tokens before storing and decrypt upon retrieval for enhanced security * refactor: enhance authentication success page with improved styling and countdown feature * refactor: add response_type parameter to OAuth redirect URI for improved compatibility * chore: update translation.json new localizations * chore: remove unused OGDialog import from OGDialogTemplate component * refactor: Actions Auth using new Dialog styling, use same component with Agents/Assistants * refactor: update removeNullishValues function to support removal of empty strings and adjust transform usage in schemas * chore: bump version of librechat-data-provider to 0.7.6991 * refactor: integrate removeNullishValues function to clean metadata before encryption in agent and assistant routes * refactor: update OAuth input fields to use 'password' type for better security * refactor: update localization placeholders for sign-in message to use double curly braces * refactor: add access_type parameter for offline access in createActionTool function * refactor: implement handleOAuthToken function for token management and encryption * feat: refresh token support * refactor: add default expiration for access token and error handling for missing token * feat: localizations for ActionAuth * refactor: set refresh token expiration to null to not expire if expiry never given * fix: prevent crash fromerror within async handleAbortError in AskController, EditController, and AgentController * feat: Action Callback URL * 🌍 i18n: Update translation.json with latest translations * refactor: handle errors in flow state checking to prevent unhandled promise rejections * fix: improve flow state concurrency to prevent multiple token creation calls * refactor: RequestExecutor to use separate axios instance * refactor: improve concurrency flows by keeping completed state until TTL expiry * refactor: increase TTL for flow state management and adjust monitoring interval * ci: mock axios instance creation in actions spec * feat: add Babel and Jest configuration files; implement FlowStateManager tests with concurrency handling * chore: add disableOAuth prop to ActionsAuth (not implemented for Assistants yet) --------- Co-authored-by: Danny Avila <danny@librechat.ai> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
109 lines
3.5 KiB
TypeScript
109 lines
3.5 KiB
TypeScript
import { forwardRef, ReactNode, Ref } from 'react';
|
|
import {
|
|
OGDialogTitle,
|
|
OGDialogClose,
|
|
OGDialogFooter,
|
|
OGDialogHeader,
|
|
OGDialogContent,
|
|
OGDialogDescription,
|
|
} from './OriginalDialog';
|
|
import { useLocalize } from '~/hooks';
|
|
import { Spinner } from '../svg';
|
|
import { cn } from '~/utils/';
|
|
|
|
type SelectionProps = {
|
|
selectHandler?: () => void;
|
|
selectClasses?: string;
|
|
selectText?: string | ReactNode;
|
|
isLoading?: boolean;
|
|
};
|
|
|
|
type DialogTemplateProps = {
|
|
title: string;
|
|
description?: string;
|
|
main?: ReactNode;
|
|
buttons?: ReactNode;
|
|
leftButtons?: ReactNode;
|
|
selection?: SelectionProps;
|
|
className?: string;
|
|
overlayClassName?: string;
|
|
headerClassName?: string;
|
|
mainClassName?: string;
|
|
footerClassName?: string;
|
|
showCloseButton?: boolean;
|
|
showCancelButton?: boolean;
|
|
onClose?: () => void;
|
|
};
|
|
|
|
const OGDialogTemplate = forwardRef((props: DialogTemplateProps, ref: Ref<HTMLDivElement>) => {
|
|
const localize = useLocalize();
|
|
const {
|
|
title,
|
|
main,
|
|
buttons,
|
|
selection,
|
|
className,
|
|
leftButtons,
|
|
description = '',
|
|
mainClassName,
|
|
headerClassName,
|
|
footerClassName,
|
|
showCloseButton,
|
|
overlayClassName,
|
|
showCancelButton = true,
|
|
} = props;
|
|
const { selectHandler, selectClasses, selectText, isLoading } = selection || {};
|
|
const Cancel = localize('com_ui_cancel');
|
|
|
|
const defaultSelect =
|
|
'bg-gray-800 text-white transition-colors hover:bg-gray-700 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-gray-200 dark:text-gray-800 dark:hover:bg-gray-200';
|
|
return (
|
|
<OGDialogContent
|
|
overlayClassName={overlayClassName}
|
|
showCloseButton={showCloseButton}
|
|
ref={ref}
|
|
className={cn('w-11/12 border-none bg-background text-foreground', className ?? '')}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<OGDialogHeader className={cn(headerClassName ?? '')}>
|
|
<OGDialogTitle>{title}</OGDialogTitle>
|
|
{description && (
|
|
<OGDialogDescription className="items-center justify-center">
|
|
{description}
|
|
</OGDialogDescription>
|
|
)}
|
|
</OGDialogHeader>
|
|
<div className={cn('px-0 py-2', mainClassName)}>{main != null ? main : null}</div>
|
|
<OGDialogFooter className={footerClassName}>
|
|
<div>
|
|
{leftButtons != null ? (
|
|
<div className="mt-3 flex h-auto gap-3 max-sm:w-full max-sm:flex-col sm:mt-0 sm:flex-row">
|
|
{leftButtons}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
<div className="flex h-auto gap-3 max-sm:w-full max-sm:flex-col sm:flex-row">
|
|
{buttons != null ? buttons : null}
|
|
{showCancelButton && (
|
|
<OGDialogClose className="btn btn-neutral border-token-border-light relative justify-center rounded-lg text-sm ring-offset-2 focus:ring-2 focus:ring-black dark:ring-offset-0 max-sm:order-last max-sm:w-full sm:order-first">
|
|
{Cancel}
|
|
</OGDialogClose>
|
|
)}
|
|
{selection ? (
|
|
<OGDialogClose
|
|
onClick={selectHandler}
|
|
disabled={isLoading}
|
|
className={`${
|
|
selectClasses ?? defaultSelect
|
|
} flex h-10 items-center justify-center rounded-lg border-none px-4 py-2 text-sm disabled:opacity-80 max-sm:order-first max-sm:w-full sm:order-none`}
|
|
>
|
|
{isLoading === true ? <Spinner className="size-4 text-white" /> : selectText}
|
|
</OGDialogClose>
|
|
) : null}
|
|
</div>
|
|
</OGDialogFooter>
|
|
</OGDialogContent>
|
|
);
|
|
});
|
|
|
|
export default OGDialogTemplate;
|