mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-06 06:48:21 +00:00
Merge pull request #6389 from ToolJet/copilot-scope-changed
improvement - changing scope of API key from user to workspace
This commit is contained in:
commit
f8df7d1568
12 changed files with 172 additions and 86 deletions
|
|
@ -8,6 +8,7 @@ export const ApiKeyContainer = ({
|
|||
isLoading = false,
|
||||
darkMode,
|
||||
isCopilotEnabled,
|
||||
isAdmin = false,
|
||||
}) => {
|
||||
const [inputValue, setInputValue] = useState(copilotApiKey);
|
||||
|
||||
|
|
@ -19,57 +20,82 @@ export const ApiKeyContainer = ({
|
|||
setInputValue(copilotApiKey);
|
||||
}, [copilotApiKey]);
|
||||
|
||||
return (
|
||||
<div className="container-xl mt-3">
|
||||
<div className="row">
|
||||
<small className="text-green">
|
||||
<img className="encrypted-icon" src="assets/images/icons/padlock2.svg" width="12" height="12" />
|
||||
<span className="text-success mx-2 font-500">API KEY</span>
|
||||
</small>
|
||||
<div className="mb-3 col-6">
|
||||
<input
|
||||
disabled={!isCopilotEnabled}
|
||||
type="password"
|
||||
class="form-control mt-2"
|
||||
name="example-text-input"
|
||||
placeholder=""
|
||||
value={inputValue}
|
||||
onChange={handleOnchange}
|
||||
/>
|
||||
const AdminInfoComponent = () => {
|
||||
return (
|
||||
<>
|
||||
<h4 class="alert-title"> Don't have an API key?</h4>
|
||||
<div class="text-muted">
|
||||
<strong style={{ fontWeight: 700, color: '#3E63DD' }}>ToolJet Copilot </strong>
|
||||
is currently in <strong style={{ fontWeight: 700, color: '#3E63DD' }}>beta</strong> and provided on request.
|
||||
Join our waitlist to be notified when API keys become available, or sign up for beta access to get started
|
||||
today.
|
||||
</div>
|
||||
<div className="col-auto mt-1">
|
||||
<div className="mt-2 w-25">
|
||||
<Button
|
||||
onClick={() => handleOnSave(inputValue)}
|
||||
onClick={() => window.open('https://tooljet.com/copilot', '_blank')}
|
||||
darkMode={darkMode}
|
||||
size="md"
|
||||
isLoading={isLoading}
|
||||
styles={{ backgroundColor: '#3E63DD', color: '#fff' }}
|
||||
disabled={!isCopilotEnabled}
|
||||
size="sm"
|
||||
styles={{ width: '100%', fontSize: '12px', fontWeight: 500, borderColor: darkMode && 'transparent' }}
|
||||
>
|
||||
<Button.Content title={'Save'} iconSrc={'assets/images/icons/save.svg'} />
|
||||
<Button.Content title={' Sign up for Beta Access'} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container-xl mt-3">
|
||||
{isAdmin && (
|
||||
<div className="row">
|
||||
<small className="text-green">
|
||||
<img className="encrypted-icon" src="assets/images/icons/padlock2.svg" width="12" height="12" />
|
||||
<span className="text-success mx-2 font-500">API KEY</span>
|
||||
</small>
|
||||
<div className="mb-3 col-6">
|
||||
<input
|
||||
disabled={!isCopilotEnabled}
|
||||
type="password"
|
||||
class="form-control mt-2"
|
||||
name="example-text-input"
|
||||
placeholder=""
|
||||
value={inputValue}
|
||||
onChange={handleOnchange}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-auto mt-1">
|
||||
<Button
|
||||
onClick={() => handleOnSave(inputValue)}
|
||||
darkMode={darkMode}
|
||||
size="md"
|
||||
isLoading={isLoading}
|
||||
styles={{ backgroundColor: '#3E63DD', color: '#fff' }}
|
||||
disabled={!isCopilotEnabled}
|
||||
>
|
||||
<Button.Content title={'Save'} iconSrc={'assets/images/icons/save.svg'} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="alert-container">
|
||||
<Alert svg="alert-info" cls="copilot-alert" data-cy={`copilot-alert-info`}>
|
||||
<h4 class="alert-title"> Don't have an API key?</h4>
|
||||
<div class="text-muted">
|
||||
<strong style={{ fontWeight: 700, color: '#3E63DD' }}>ToolJet Copilot </strong>
|
||||
is currently in <strong style={{ fontWeight: 700, color: '#3E63DD' }}>beta</strong> and provided on request.
|
||||
Join our waitlist to be notified when API keys become available, or sign up for beta access to get started
|
||||
today.
|
||||
</div>
|
||||
<div className="mt-2 w-25">
|
||||
<Button
|
||||
onClick={() => window.open('https://tooljet.com/copilot', '_blank')}
|
||||
darkMode={darkMode}
|
||||
size="sm"
|
||||
styles={{ width: '100%', fontSize: '12px', fontWeight: 500, borderColor: darkMode && 'transparent' }}
|
||||
>
|
||||
<Button.Content title={' Sign up for Beta Access'} />
|
||||
</Button>
|
||||
</div>
|
||||
{isAdmin ? (
|
||||
<AdminInfoComponent />
|
||||
) : (
|
||||
<>
|
||||
<div class="text-muted">
|
||||
<strong style={{ fontWeight: 700, color: '#3E63DD' }}>ToolJet Copilot </strong>
|
||||
is currently in <strong style={{ fontWeight: 700, color: '#3E63DD' }}>beta</strong> and provided on
|
||||
request. Join our waitlist to be notified when API keys become available, or sign up for beta access to
|
||||
get started today.
|
||||
</div>
|
||||
<div class="text-muted mt-2">
|
||||
<strong style={{ fontWeight: 700, color: '#df4759' }}>Please note : </strong>
|
||||
Copilot functionality is dependent on your workspace admin completing the setup process.
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Alert>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ import { Button } from '@/_ui/LeftSidebar';
|
|||
import { useLocalStorageState } from '@/_hooks/use-local-storage';
|
||||
|
||||
export const CopilotSetting = () => {
|
||||
const { current_organization_id } = authenticationService.currentSessionValue;
|
||||
const { current_organization_id, current_organization_name, admin } = authenticationService.currentSessionValue;
|
||||
const currentOrgName = current_organization_name.replace(/\s/g, '').toLowerCase();
|
||||
|
||||
const [copilotApiKey, setCopilotApiKey] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [state, setState] = useLocalStorageState(`copilotEnabled-${current_organization_id}`, false);
|
||||
const [state, setState] = useLocalStorageState(`copilotEnabled-${currentOrgName}`, false);
|
||||
const [copilotWorkspaceVarId, set] = useState(null);
|
||||
|
||||
const saveCopilotApiKey = async (apikey) => {
|
||||
|
|
@ -30,7 +32,10 @@ export const CopilotSetting = () => {
|
|||
console.log(err);
|
||||
return toast.error('Something went wrong');
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
orgEnvironmentVariableService.create(`copilot_enabled-${current_organization_id}`, 'true', 'client', false);
|
||||
});
|
||||
}
|
||||
|
||||
if (isCopilotApiKeyPresent === true && copilotWorkspaceVarId) {
|
||||
|
|
@ -58,7 +63,7 @@ export const CopilotSetting = () => {
|
|||
const validateApiKey = (apiKey) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
copilotService
|
||||
.validateCopilotAPIKey(apiKey)
|
||||
.validateCopilotAPIKey(apiKey, current_organization_id)
|
||||
.then(({ status }) => {
|
||||
if (status === 'ok') {
|
||||
return resolve(true);
|
||||
|
|
@ -72,33 +77,56 @@ export const CopilotSetting = () => {
|
|||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
|
||||
orgEnvironmentVariableService.getVariables().then((data) => {
|
||||
const isCopilotApiKeyPresent = data.variables.some(
|
||||
(variable) => variable.variable_name === `copilot_api_key-${current_organization_id}`
|
||||
);
|
||||
|
||||
const shouldUpdate = isCopilotApiKeyPresent;
|
||||
if (shouldUpdate) {
|
||||
const copilotVariableId = data.variables.find(
|
||||
(variable) => variable.variable_name === `copilot_api_key-${current_organization_id}`
|
||||
)?.id;
|
||||
const key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
|
||||
set(copilotVariableId);
|
||||
setCopilotApiKey(key);
|
||||
}
|
||||
const updateCopilotEnabled = (id, value, variableName) => {
|
||||
return orgEnvironmentVariableService.update(id, variableName, `${value}`).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!admin) {
|
||||
orgEnvironmentVariableService.getVariables().then((data) => {
|
||||
const { value } = data.variables.find(
|
||||
(variable) => variable.variable_name === `copilot_enabled-${current_organization_id}`
|
||||
);
|
||||
|
||||
setState(value === 'true');
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (admin) {
|
||||
orgEnvironmentVariableService.getVariables().then((data) => {
|
||||
const isCopilotApiKeyPresent = data.variables.some(
|
||||
(variable) => variable.variable_name === `copilot_api_key-${current_organization_id}`
|
||||
);
|
||||
|
||||
const { id, variable_name, value } = data.variables.find(
|
||||
(variable) => variable.variable_name === `copilot_enabled-${current_organization_id}`
|
||||
);
|
||||
|
||||
if (value !== `${state}`) updateCopilotEnabled(id, state, variable_name);
|
||||
|
||||
const shouldUpdate = state && isCopilotApiKeyPresent;
|
||||
if (shouldUpdate) {
|
||||
const copilotVariableId = data.variables.find(
|
||||
(variable) => variable.variable_name === `copilot_api_key-${current_organization_id}`
|
||||
)?.id;
|
||||
const key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
|
||||
set(copilotVariableId);
|
||||
setCopilotApiKey(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
setCopilotApiKey('');
|
||||
set(null);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
}, [state]);
|
||||
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
|
||||
|
|
@ -111,7 +139,7 @@ export const CopilotSetting = () => {
|
|||
padding: '4rem',
|
||||
}}
|
||||
>
|
||||
<Container isCopilotEnabled={state} handleCopilotToggle={handleCopilotToggle}>
|
||||
<Container isCopilotEnabled={state} handleCopilotToggle={handleCopilotToggle} isAdmin={admin}>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<ApiKeyContainer
|
||||
|
|
@ -120,6 +148,7 @@ export const CopilotSetting = () => {
|
|||
isLoading={isLoading}
|
||||
darkMode={darkMode}
|
||||
isCopilotEnabled={state}
|
||||
isAdmin={admin}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -130,7 +159,7 @@ export const CopilotSetting = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const Container = ({ children, isCopilotEnabled, handleCopilotToggle, darkMode }) => {
|
||||
const Container = ({ children, isCopilotEnabled, handleCopilotToggle, darkMode, isAdmin }) => {
|
||||
return (
|
||||
<div className="card p-2 card-container">
|
||||
<div className="card-header row">
|
||||
|
|
@ -151,6 +180,7 @@ const Container = ({ children, isCopilotEnabled, handleCopilotToggle, darkMode }
|
|||
toggleSwitchFunction={handleCopilotToggle}
|
||||
action="enableTransformation"
|
||||
dataCy={'copilot'}
|
||||
disabled={!isAdmin}
|
||||
/>
|
||||
|
||||
<span className="mx-2 mt-3 font-weight-400 tranformation-label" data-cy={'label-query-transformation'}>
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ export function CodeHinter({
|
|||
popOverCallback,
|
||||
cyLabel = '',
|
||||
callgpt = () => null,
|
||||
isCopilotEnabled = false,
|
||||
}) {
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
const options = {
|
||||
|
|
@ -323,6 +324,7 @@ export function CodeHinter({
|
|||
/>
|
||||
)}
|
||||
<CodeHinter.Portal
|
||||
isCopilotEnabled={isCopilotEnabled}
|
||||
isOpen={isOpen}
|
||||
callback={setIsOpen}
|
||||
componentName={componentName}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Tooltip as ReactTooltip } from 'react-tooltip';
|
||||
|
||||
export const CustomToggleSwitch = ({
|
||||
isChecked,
|
||||
|
|
@ -7,9 +8,14 @@ export const CustomToggleSwitch = ({
|
|||
darkMode = false,
|
||||
label = '',
|
||||
dataCy = '',
|
||||
disabled = false,
|
||||
}) => {
|
||||
return (
|
||||
<div className={`custom-toggle-switch d-flex col gap-2 align-items-center ${darkMode && 'theme-dark'}`}>
|
||||
<div
|
||||
data-tooltip-id="tooltip-for-active-copilot"
|
||||
data-tooltip-content="Only workspace admins can enable or disable Copilot."
|
||||
className={`custom-toggle-switch d-flex col gap-2 align-items-center ${darkMode && 'theme-dark'}`}
|
||||
>
|
||||
<label className="switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
|
|
@ -23,6 +29,7 @@ export const CustomToggleSwitch = ({
|
|||
}
|
||||
}}
|
||||
data-cy={`${dataCy}-toggle-switch`}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<label htmlFor={action} className="slider round"></label>
|
||||
</label>
|
||||
|
|
@ -31,6 +38,13 @@ export const CustomToggleSwitch = ({
|
|||
{label}
|
||||
</span>
|
||||
)}
|
||||
{disabled && dataCy === 'copilot' && (
|
||||
<ReactTooltip
|
||||
id="tooltip-for-active-copilot"
|
||||
className="tooltip"
|
||||
style={{ backgroundColor: '#e6eefe', color: '#222' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import { authenticationService } from '@/_services';
|
|||
|
||||
export const Transformation = ({ changeOption, currentState, options, darkMode, queryId }) => {
|
||||
const { t } = useTranslation();
|
||||
const { current_organization_id } = authenticationService.currentSessionValue;
|
||||
const { current_organization_name } = authenticationService.currentSessionValue;
|
||||
const currentOrgName = current_organization_name.replace(/\s/g, '').toLowerCase();
|
||||
|
||||
const [lang, setLang] = React.useState(options?.transformationLanguage ?? 'javascript');
|
||||
|
||||
|
|
@ -39,7 +40,7 @@ return [row for row in data if row['amount'] > 1000]
|
|||
const [state, setState] = useLocalStorageState('transformation', defaultValue);
|
||||
|
||||
const [fetchingRecommendation, setFetchingRecommendation] = useState(false);
|
||||
const isCopilotEnabled = localStorage.getItem(`copilotEnabled-${current_organization_id}`) === 'true';
|
||||
const isCopilotEnabled = localStorage.getItem(`copilotEnabled-${currentOrgName}`) === 'true';
|
||||
|
||||
const handleCallToGPT = async () => {
|
||||
setFetchingRecommendation(true);
|
||||
|
|
@ -291,7 +292,7 @@ return [row for row in data if row['amount'] > 1000]
|
|||
styles={{ width: '100%', fontSize: '12px', fontWeight: 500, borderColor: darkMode && 'transparent' }}
|
||||
disabled={!isCopilotEnabled}
|
||||
>
|
||||
<Button.Content title={'Generate code ⌘+L'} />
|
||||
<Button.Content title={'Generate code'} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
|
@ -317,6 +318,7 @@ return [row for row in data if row['amount'] > 1000]
|
|||
componentName={`transformation`}
|
||||
cyLabel={'transformation-input'}
|
||||
callgpt={handleCallToGPT}
|
||||
isCopilotEnabled={isCopilotEnabled}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -74,9 +74,7 @@ class ManageOrgVarsComponent extends React.Component {
|
|||
});
|
||||
|
||||
orgEnvironmentVariableService.getVariables().then((data) => {
|
||||
const variables = _.cloneDeep(data.variables)?.filter(
|
||||
({ variable_name }) => !/copilot_api_key/.test(variable_name)
|
||||
);
|
||||
const variables = _.cloneDeep(data.variables)?.filter(({ variable_name }) => !/copilot_/.test(variable_name));
|
||||
this.setState({
|
||||
variables: variables,
|
||||
isLoading: false,
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export function OrganizationSettings(props) {
|
|||
{sideBarNavs.map((item, index) => {
|
||||
return (
|
||||
<>
|
||||
{(admin || item == 'Workspace variables') && (
|
||||
{(admin || item == 'Workspace variables' || item == 'Copilot') && (
|
||||
<FolderList
|
||||
className="workspace-settings-nav-items"
|
||||
key={index}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import { Rnd } from 'react-rnd';
|
|||
import { Button } from '@/_ui/LeftSidebar';
|
||||
|
||||
const Portal = ({ children, ...restProps }) => {
|
||||
const { isOpen, trigger, styles, className, componentName, dragResizePortal, callgpt } = restProps;
|
||||
const { isOpen, trigger, styles, className, componentName, dragResizePortal, callgpt, isCopilotEnabled } = restProps;
|
||||
|
||||
const [name, setName] = React.useState(componentName);
|
||||
const handleClose = (e) => {
|
||||
e.stopPropagation();
|
||||
|
|
@ -45,6 +46,7 @@ const Portal = ({ children, ...restProps }) => {
|
|||
componentName={name}
|
||||
dragResizePortal={dragResizePortal}
|
||||
callgpt={callgpt}
|
||||
isCopilotEnabled={isCopilotEnabled}
|
||||
>
|
||||
{children}
|
||||
</Portal.Modal>
|
||||
|
|
@ -57,7 +59,17 @@ const Container = ({ children, ...restProps }) => {
|
|||
return <ReactPortal {...restProps}>{children}</ReactPortal>;
|
||||
};
|
||||
|
||||
const Modal = ({ children, handleClose, portalStyles, styles, componentName, darkMode, dragResizePortal, callgpt }) => {
|
||||
const Modal = ({
|
||||
children,
|
||||
handleClose,
|
||||
portalStyles,
|
||||
styles,
|
||||
componentName,
|
||||
darkMode,
|
||||
dragResizePortal,
|
||||
callgpt,
|
||||
isCopilotEnabled,
|
||||
}) => {
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
const handleCallGpt = () => {
|
||||
|
|
@ -66,7 +78,6 @@ const Modal = ({ children, handleClose, portalStyles, styles, componentName, dar
|
|||
callgpt().then(() => setLoading(false));
|
||||
};
|
||||
|
||||
const isCopilotEnabled = localStorage.getItem('copilotEnabled') === 'true';
|
||||
const includeGPT = ['Runjs', 'Runpy', 'transformation'].includes(componentName) && isCopilotEnabled;
|
||||
|
||||
const renderModalContent = () => (
|
||||
|
|
@ -95,7 +106,7 @@ const Modal = ({ children, handleClose, portalStyles, styles, componentName, dar
|
|||
classNames={`${loading ? (darkMode ? 'btn-loading' : 'button-loading') : ''}`}
|
||||
styles={{ width: '100%', fontSize: '12px', fontWeight: 500, borderColor: darkMode && 'transparent' }}
|
||||
>
|
||||
<Button.Content title={'Generate code ⌘+L'} />
|
||||
<Button.Content title={'Generate code'} />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ const usePortal = ({ children, ...restProps }) => {
|
|||
selectors = {},
|
||||
dragResizePortal = false,
|
||||
callgpt,
|
||||
isCopilotEnabled = false,
|
||||
} = restProps;
|
||||
|
||||
const renderCustomComponent = ({ component, ...restProps }) => {
|
||||
|
|
@ -38,6 +39,7 @@ const usePortal = ({ children, ...restProps }) => {
|
|||
componentName={componentName}
|
||||
dragResizePortal={dragResizePortal}
|
||||
callgpt={callgpt}
|
||||
isCopilotEnabled={isCopilotEnabled}
|
||||
>
|
||||
<div className={`editor-container ${optionalProps.cls ?? ''}`} key={key}>
|
||||
{React.cloneElement(children, { ...styleProps })}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,10 @@ async function getCopilotRecommendations(options) {
|
|||
return data || {};
|
||||
}
|
||||
|
||||
function validateCopilotAPIKey(key) {
|
||||
function validateCopilotAPIKey(key, organizationId) {
|
||||
const body = {
|
||||
secretKey: key,
|
||||
organizationId,
|
||||
};
|
||||
|
||||
const requestOptions = { method: 'POST', headers: authHeader(), credentials: 'include', body: JSON.stringify(body) };
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export class CopilotController {
|
|||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('api-key')
|
||||
async validateCopilotAPIKey(@User() user, @Body() body: { secretKey: string }) {
|
||||
return await this.copilotService.validateCopilotAPIKey(user.id, body.secretKey);
|
||||
async validateCopilotAPIKey(@User() user, @Body() body: { secretKey: string; organizationId: string }) {
|
||||
return await this.copilotService.validateCopilotAPIKey(body.organizationId, body.secretKey);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export class CopilotService {
|
|||
query: query,
|
||||
context: context,
|
||||
language: language,
|
||||
userId: userId,
|
||||
workspaceId: orgnaizationId,
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -42,13 +42,13 @@ export class CopilotService {
|
|||
};
|
||||
}
|
||||
|
||||
async validateCopilotAPIKey(userId: string, secretKey: string) {
|
||||
async validateCopilotAPIKey(workspaceId: string, secretKey: string) {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ userId: userId, action: 'get', apiKey: secretKey }),
|
||||
body: JSON.stringify({ workspaceId: workspaceId, action: 'get', apiKey: secretKey }),
|
||||
};
|
||||
|
||||
const response = await fetch(`${process.env.COPILOT_API_ENDPOINT}/api-key`, options);
|
||||
|
|
|
|||
Loading…
Reference in a new issue