mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
🚫👤feat: delete user from UI (#1526)
* initial commit * fix: UserController bugs; fix: lint errors * fix: delete files * language support * style(DeleteAccount): update to the latest style * style: fix after merge main * chore: Add canDeleteAccount middleware for user deletion endpoint * chore: renamed to ALLOW_ACCOUNT_DELETION * fix(canDeleteAccount): use uppercase admin role * chore: imports order * chore: Enable account deletion by default if omitted/commented out * chore: Add logging for user account deletion * chore: Bump data-provider package version to 0.6.6 * chore: Import Transaction model in UserController * chore: Update CONFIG_VERSION to 1.1.4 * chore: Update user account deletion logging * chore: Refactor user account deletion logic --------- Co-authored-by: Berry-13 <root@Berry> Co-authored-by: Danny Avila <messagedaniel@protonmail.com> Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
parent
f69b317171
commit
a7f5b57272
19 changed files with 348 additions and 17 deletions
|
|
@ -319,6 +319,7 @@ ALLOW_EMAIL_LOGIN=true
|
|||
ALLOW_REGISTRATION=true
|
||||
ALLOW_SOCIAL_LOGIN=false
|
||||
ALLOW_SOCIAL_REGISTRATION=false
|
||||
# ALLOW_ACCOUNT_DELETION=true # note: enabled by default if omitted/commented out
|
||||
|
||||
SESSION_EXPIRY=1000 * 60 * 15
|
||||
REFRESH_TOKEN_EXPIRY=(1000 * 60 * 60 * 24) * 7
|
||||
|
|
|
|||
|
|
@ -97,8 +97,12 @@ const deleteFileByFilter = async (filter) => {
|
|||
* @param {Array<string>} file_ids - The unique identifiers of the files to delete.
|
||||
* @returns {Promise<Object>} A promise that resolves to the result of the deletion operation.
|
||||
*/
|
||||
const deleteFiles = async (file_ids) => {
|
||||
return await File.deleteMany({ file_id: { $in: file_ids } });
|
||||
const deleteFiles = async (file_ids, user) => {
|
||||
let deleteQuery = { file_id: { $in: file_ids } };
|
||||
if (user) {
|
||||
deleteQuery = { user: user };
|
||||
}
|
||||
return await File.deleteMany(deleteQuery);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,15 @@
|
|||
const { updateUserPluginsService } = require('~/server/services/UserService');
|
||||
const {
|
||||
User,
|
||||
Session,
|
||||
Balance,
|
||||
deleteFiles,
|
||||
deleteConvos,
|
||||
deletePresets,
|
||||
deleteMessages,
|
||||
} = require('~/models');
|
||||
const { updateUserPluginAuth, deleteUserPluginAuth } = require('~/server/services/PluginService');
|
||||
const { updateUserPluginsService, deleteUserKey } = require('~/server/services/UserService');
|
||||
const { Transaction } = require('~/models/Transaction');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
const getUserController = async (req, res) => {
|
||||
|
|
@ -53,7 +63,30 @@ const updateUserPluginsController = async (req, res) => {
|
|||
}
|
||||
};
|
||||
|
||||
const deleteUserController = async (req, res) => {
|
||||
const { user } = req;
|
||||
|
||||
try {
|
||||
await deleteMessages({ user: user.id }); // delete user messages
|
||||
await Session.deleteMany({ user: user.id }); // delete user sessions
|
||||
await Transaction.deleteMany({ user: user.id }); // delete user transactions
|
||||
await deleteUserKey({ userId: user.id, all: true }); // delete user keys
|
||||
await Balance.deleteMany({ user: user._id }); // delete user balances
|
||||
await deletePresets(user.id); // delete user presets
|
||||
await deleteConvos(user.id); // delete user convos
|
||||
await deleteUserPluginAuth(user.id, null, true); // delete user plugin auth
|
||||
await User.deleteOne({ _id: user.id }); // delete user
|
||||
await deleteFiles(null, user.id); // delete user files
|
||||
logger.info(`User deleted account. Email: ${user.email} ID: ${user.id}`);
|
||||
res.status(200).send({ message: 'User deleted' });
|
||||
} catch (err) {
|
||||
logger.error('[deleteUserController]', err);
|
||||
res.status(500).send({ message: err.message });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getUserController,
|
||||
updateUserPluginsController,
|
||||
deleteUserController,
|
||||
};
|
||||
|
|
|
|||
27
api/server/middleware/canDeleteAccount.js
Normal file
27
api/server/middleware/canDeleteAccount.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
const { isEnabled } = require('~/server/utils');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
/**
|
||||
* Checks if the user can delete their account
|
||||
*
|
||||
* @async
|
||||
* @function
|
||||
* @param {Object} req - Express request object
|
||||
* @param {Object} res - Express response object
|
||||
* @param {Function} next - Next middleware function
|
||||
*
|
||||
* @returns {Promise<function|Object>} - Returns a Promise which when resolved calls next middleware if the user can delete their account
|
||||
*/
|
||||
|
||||
const canDeleteAccount = async (req, res, next = () => {}) => {
|
||||
const { user } = req;
|
||||
const { ALLOW_ACCOUNT_DELETION = true } = process.env;
|
||||
if (user?.role === 'ADMIN' || isEnabled(ALLOW_ACCOUNT_DELETION)) {
|
||||
return next();
|
||||
} else {
|
||||
logger.error(`[User] [Delete Account] [User cannot delete account] [User: ${user?.id}]`);
|
||||
return res.status(403).send({ message: 'You do not have permission to delete this account' });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = canDeleteAccount;
|
||||
|
|
@ -20,6 +20,7 @@ const validateImageRequest = require('./validateImageRequest');
|
|||
const moderateText = require('./moderateText');
|
||||
const noIndex = require('./noIndex');
|
||||
const importLimiters = require('./importLimiters');
|
||||
const canDeleteAccount = require('./canDeleteAccount');
|
||||
|
||||
module.exports = {
|
||||
...uploadLimiters,
|
||||
|
|
@ -44,4 +45,5 @@ module.exports = {
|
|||
noIndex,
|
||||
...importLimiters,
|
||||
checkDomainAllowed,
|
||||
canDeleteAccount,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
const express = require('express');
|
||||
const requireJwtAuth = require('../middleware/requireJwtAuth');
|
||||
const { getUserController, updateUserPluginsController } = require('../controllers/UserController');
|
||||
const canDeleteAccount = require('../middleware/canDeleteAccount');
|
||||
const {
|
||||
getUserController,
|
||||
updateUserPluginsController,
|
||||
deleteUserController,
|
||||
} = require('../controllers/UserController');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/', requireJwtAuth, getUserController);
|
||||
router.post('/plugins', requireJwtAuth, updateUserPluginsController);
|
||||
router.delete('/delete', requireJwtAuth, canDeleteAccount, deleteUserController);
|
||||
|
||||
module.exports = router;
|
||||
|
|
|
|||
|
|
@ -88,7 +88,17 @@ const updateUserPluginAuth = async (userId, authField, pluginKey, value) => {
|
|||
}
|
||||
};
|
||||
|
||||
const deleteUserPluginAuth = async (userId, authField) => {
|
||||
const deleteUserPluginAuth = async (userId, authField, all = false) => {
|
||||
if (all) {
|
||||
try {
|
||||
const response = await PluginAuth.deleteMany({ userId });
|
||||
return response;
|
||||
} catch (err) {
|
||||
logger.error('[deleteUserPluginAuth]', err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return await PluginAuth.deleteOne({ userId, authField });
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ const SocialButton = ({ id, enabled, serverDomain, oauthPath, Icon, label }) =>
|
|||
|
||||
const handleMouseLeave = () => {
|
||||
setIsHovered(false);
|
||||
if (isPressed) {setIsPressed(false);}
|
||||
if (isPressed) {
|
||||
setIsPressed(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseDown = () => {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ const ClearConvos = ({ open, onOpenChange }) => {
|
|||
// Clear all conversations
|
||||
const clearConvos = () => {
|
||||
if (confirmClear) {
|
||||
console.log('Clearing conversations...');
|
||||
clearConvosMutation.mutate(
|
||||
{},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import { useRecoilState } from 'recoil';
|
||||
import * as Tabs from '@radix-ui/react-tabs';
|
||||
import { SettingsTabValues } from 'librechat-data-provider';
|
||||
import DeleteAccount from './DeleteAccount';
|
||||
import { Switch } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import Avatar from './Avatar';
|
||||
|
|
@ -28,6 +29,9 @@ function Account({ onCheckedChange }: { onCheckedChange?: (value: boolean) => vo
|
|||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<Avatar />
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
|
||||
<DeleteAccount />
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div> {localize('com_nav_user_name_display')} </div>
|
||||
<Switch
|
||||
|
|
@ -39,7 +43,6 @@ function Account({ onCheckedChange }: { onCheckedChange?: (value: boolean) => vo
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600"></div>
|
||||
</Tabs.Content>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
174
client/src/components/Nav/SettingsTabs/Account/DeleteAccount.tsx
Normal file
174
client/src/components/Nav/SettingsTabs/Account/DeleteAccount.tsx
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
import React, { useState, useCallback } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogButton,
|
||||
Input,
|
||||
} from '~/components/ui';
|
||||
import { cn, defaultTextProps, removeFocusOutlines } from '~/utils';
|
||||
import { useDeleteUserMutation } from '~/data-provider';
|
||||
import { Spinner, LockIcon } from '~/components/svg';
|
||||
import { useAuthContext } from '~/hooks/AuthContext';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
const DeleteAccount = ({ disabled = false }: { title?: string; disabled?: boolean }) => {
|
||||
const localize = useLocalize();
|
||||
const { user, logout } = useAuthContext();
|
||||
const { mutate: deleteUser, isLoading: isDeleting } = useDeleteUserMutation({
|
||||
onSuccess: () => logout(),
|
||||
});
|
||||
|
||||
const [isDialogOpen, setDialogOpen] = useState<boolean>(false);
|
||||
const [deleteInput, setDeleteInput] = useState('');
|
||||
const [emailInput, setEmailInput] = useState('');
|
||||
const [isLocked, setIsLocked] = useState(true);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
setDialogOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleDeleteUser = () => {
|
||||
if (!isLocked) {
|
||||
deleteUser(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = useCallback(
|
||||
(newEmailInput: string, newDeleteInput: string) => {
|
||||
const isEmailCorrect =
|
||||
newEmailInput.trim().toLowerCase() === user?.email?.trim().toLowerCase();
|
||||
const isDeleteInputCorrect = newDeleteInput === 'DELETE';
|
||||
setIsLocked(!(isEmailCorrect && isDeleteInputCorrect));
|
||||
},
|
||||
[user?.email],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<span>{localize('com_nav_delete_account')}</span>
|
||||
<label>
|
||||
<DialogButton
|
||||
id={'delete-user-account'}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
'btn btn-danger relative border-none bg-red-700 text-white hover:bg-red-800 dark:hover:bg-red-800',
|
||||
)}
|
||||
>
|
||||
{localize('com_ui_delete')}
|
||||
</DialogButton>
|
||||
</label>
|
||||
</div>
|
||||
<Dialog open={isDialogOpen} onOpenChange={() => setDialogOpen(false)}>
|
||||
<DialogContent
|
||||
className={cn('shadow-2xl md:h-[500px] md:w-[450px]')}
|
||||
style={{ borderRadius: '12px', padding: '20px' }}
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-lg font-medium leading-6">
|
||||
{localize('com_nav_delete_account_confirm')}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="mb-20 text-sm text-black dark:text-white">
|
||||
<ul>
|
||||
<li>{localize('com_nav_delete_warning')}</li>
|
||||
<li>{localize('com_nav_delete_data_info')}</li>
|
||||
<li>{localize('com_nav_delete_help_center')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex-col items-center justify-center">
|
||||
<div className="mb-4">
|
||||
{renderInput(
|
||||
localize('com_nav_delete_account_email_placeholder'),
|
||||
'email-confirm-input',
|
||||
user?.email || '',
|
||||
(e) => {
|
||||
setEmailInput(e.target.value);
|
||||
handleInputChange(e.target.value, deleteInput);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
{renderInput(
|
||||
localize('com_nav_delete_account_confirm_placeholder'),
|
||||
'delete-confirm-input',
|
||||
'',
|
||||
(e) => {
|
||||
setDeleteInput(e.target.value);
|
||||
handleInputChange(emailInput, e.target.value);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
{renderDeleteButton(handleDeleteUser, isDeleting, isLocked, localize)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const renderInput = (
|
||||
label: string,
|
||||
id: string,
|
||||
value: string,
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void,
|
||||
) => (
|
||||
<div className="mb-4">
|
||||
<label className="mb-1 block text-sm font-medium text-black dark:text-white">{label}</label>
|
||||
<Input
|
||||
id={id}
|
||||
onChange={onChange}
|
||||
placeholder={value}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'h-10 max-h-10 w-full max-w-full rounded-md bg-white px-3 py-2',
|
||||
removeFocusOutlines,
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderDeleteButton = (
|
||||
handleDeleteUser: () => void,
|
||||
isDeleting: boolean,
|
||||
isLocked: boolean,
|
||||
localize: (key: string) => string,
|
||||
) => (
|
||||
<button
|
||||
className={cn(
|
||||
'mt-4 flex w-full items-center justify-center rounded-lg px-4 py-2 transition-colors duration-200',
|
||||
isLocked
|
||||
? 'cursor-not-allowed bg-gray-200 text-gray-300 dark:bg-gray-500 dark:text-gray-600'
|
||||
: isDeleting
|
||||
? 'cursor-not-allowed bg-gray-100 text-gray-700 dark:bg-gray-400 dark:text-gray-700'
|
||||
: 'bg-red-700 text-white hover:bg-red-800 ',
|
||||
)}
|
||||
onClick={handleDeleteUser}
|
||||
disabled={isDeleting || isLocked}
|
||||
>
|
||||
{isDeleting ? (
|
||||
<div className="flex h-6 justify-center">
|
||||
<Spinner className="icon-sm m-auto" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{isLocked ? (
|
||||
<>
|
||||
<LockIcon />
|
||||
<span className="ml-2">{localize('com_ui_locked')}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<LockIcon />
|
||||
<span className="ml-2">{localize('com_nav_delete_account_button')}</span>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
|
||||
export default DeleteAccount;
|
||||
19
client/src/components/svg/LockIcon.tsx
Normal file
19
client/src/components/svg/LockIcon.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
export default function LockIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="lucide lucide-lock"
|
||||
>
|
||||
<rect width="18" height="11" x="3" y="11" rx="2" ry="2" />
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
|
@ -46,6 +46,7 @@ export { default as VolumeIcon } from './VolumeIcon';
|
|||
export { default as VolumeMuteIcon } from './VolumeMuteIcon';
|
||||
export { default as SendMessageIcon } from './SendMessageIcon';
|
||||
export { default as UserIcon } from './UserIcon';
|
||||
export { default as LockIcon } from './LockIcon';
|
||||
export { default as NewChatIcon } from './NewChatIcon';
|
||||
export { default as ExperimentIcon } from './ExperimentIcon';
|
||||
export { default as GoogleIconChat } from './GoogleIconChat';
|
||||
|
|
|
|||
|
|
@ -3,22 +3,22 @@ import {
|
|||
LocalStorageKeys,
|
||||
defaultAssistantsVersion,
|
||||
} from 'librechat-data-provider';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { dataService, MutationKeys, QueryKeys, defaultOrderQuery } from 'librechat-data-provider';
|
||||
import type { UseMutationResult } from '@tanstack/react-query';
|
||||
import type t from 'librechat-data-provider';
|
||||
import {
|
||||
addSharedLink,
|
||||
addConversation,
|
||||
deleteSharedLink,
|
||||
updateConvoFields,
|
||||
updateConversation,
|
||||
deleteConversation,
|
||||
updateConvoFields,
|
||||
deleteSharedLink,
|
||||
addSharedLink,
|
||||
} from '~/utils';
|
||||
import { dataService, MutationKeys, QueryKeys, defaultOrderQuery } from 'librechat-data-provider';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import store from '~/store';
|
||||
import { normalizeData } from '~/utils/collection';
|
||||
import { useConversationsInfiniteQuery, useSharedLinksInfiniteQuery } from './queries';
|
||||
import { normalizeData } from '~/utils/collection';
|
||||
import store from '~/store';
|
||||
|
||||
/** Conversations */
|
||||
export const useGenTitleMutation = (): UseMutationResult<
|
||||
|
|
@ -609,6 +609,30 @@ export const useUploadAvatarMutation = (
|
|||
});
|
||||
};
|
||||
|
||||
export const useDeleteUserMutation = (
|
||||
options?: t.MutationOptions<unknown, undefined>,
|
||||
): UseMutationResult<unknown, unknown, undefined, unknown> => {
|
||||
const queryClient = useQueryClient();
|
||||
const setDefaultPreset = useSetRecoilState(store.defaultPreset);
|
||||
return useMutation([MutationKeys.deleteUser], {
|
||||
mutationFn: () => dataService.deleteUser(),
|
||||
|
||||
...(options || {}),
|
||||
onSuccess: (...args) => {
|
||||
options?.onSuccess?.(...args);
|
||||
},
|
||||
onMutate: (...args) => {
|
||||
setDefaultPreset(null);
|
||||
queryClient.removeQueries();
|
||||
localStorage.removeItem(LocalStorageKeys.LAST_CONVO_SETUP);
|
||||
localStorage.removeItem(LocalStorageKeys.LAST_MODEL);
|
||||
localStorage.removeItem(LocalStorageKeys.LAST_TOOLS);
|
||||
localStorage.removeItem(LocalStorageKeys.FILES_TO_DELETE);
|
||||
options?.onMutate?.(...args);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/* Speech to text */
|
||||
export const useSpeechToTextMutation = (
|
||||
options?: t.SpeechToTextOptions,
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ export default {
|
|||
com_ui_preview: 'Preview',
|
||||
com_ui_upload: 'Upload',
|
||||
com_ui_connect: 'Connect',
|
||||
com_ui_locked: 'Locked',
|
||||
com_ui_upload_delay:
|
||||
'Uploading "{0}" is taking more time than anticipated. Please wait while the file finishes indexing for retrieval.',
|
||||
com_ui_privacy_policy: 'Privacy policy',
|
||||
|
|
@ -546,6 +547,14 @@ export default {
|
|||
com_nav_help_faq: 'Help & FAQ',
|
||||
com_nav_settings: 'Settings',
|
||||
com_nav_search_placeholder: 'Search messages',
|
||||
com_nav_delete_account: 'Delete account',
|
||||
com_nav_delete_account_confirm: 'Delete account - are you sure?',
|
||||
com_nav_delete_account_button: 'Permanently delete my account',
|
||||
com_nav_delete_account_email_placeholder: 'Please enter your account email',
|
||||
com_nav_delete_account_confirm_placeholder: 'To proceed, type "DELETE" in the input field below',
|
||||
com_nav_delete_warning: 'WARNING: This will permanently delete your account.',
|
||||
com_nav_delete_data_info: 'All your data will be deleted.',
|
||||
com_nav_delete_help_center: 'For more information, please visit our Help Center.',
|
||||
com_nav_conversation_mode: 'Conversation Mode',
|
||||
com_nav_auto_send_text: 'Auto send text (after 3 sec)',
|
||||
com_nav_auto_transcribe_audio: 'Auto transcribe audio',
|
||||
|
|
|
|||
|
|
@ -212,6 +212,7 @@ export default {
|
|||
com_ui_preview: 'Anteprima',
|
||||
com_ui_upload: 'Carica',
|
||||
com_ui_connect: 'Connetti',
|
||||
com_ui_locked: 'Bloccato',
|
||||
com_ui_upload_delay:
|
||||
'Il caricamento di "{0}" sta richiedendo più tempo del previsto. Attendi il completamento dell\'indicizzazione per il recupero.',
|
||||
com_ui_privacy_policy: 'Informativa sulla privacy',
|
||||
|
|
@ -520,8 +521,17 @@ export default {
|
|||
com_nav_help_faq: 'Guida e FAQ',
|
||||
com_nav_settings: 'Impostazioni',
|
||||
com_nav_search_placeholder: 'Cerca messaggi',
|
||||
com_nav_setting_general: 'Generali',
|
||||
com_nav_setting_beta: 'Funzionalità beta',
|
||||
com_nav_delete_account: 'Elimina account',
|
||||
com_nav_delete_account_confirm: 'Sei sicuro di voler eliminare il tuo account?',
|
||||
com_nav_delete_account_button: 'Elimina permanentemente il mio account',
|
||||
com_nav_delete_account_email_placeholder: 'Inserisci la tua email',
|
||||
com_nav_delete_account_confirm_placeholder:
|
||||
'Per procedere, digita "DELETE" nel campo di input sottostante',
|
||||
com_dialog_delete_warning: 'ATTENZIONE: Questo cancellerà permanentemente il tuo account.',
|
||||
com_dialog_delete_data_info: 'Tutti i tuoi dati verranno eliminati.',
|
||||
com_dialog_delete_help_center: 'Per più informazioni, visita il nostro centro assistenza.',
|
||||
com_nav_setting_general: 'Generale',
|
||||
com_nav_setting_beta: 'Funzioni Beta',
|
||||
com_nav_setting_data: 'Controlli dati',
|
||||
com_nav_setting_speech: 'Voce',
|
||||
com_nav_setting_account: 'Account',
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ export const balance = () => '/api/balance';
|
|||
|
||||
export const userPlugins = () => '/api/user/plugins';
|
||||
|
||||
export const deleteUser = () => '/api/user/delete';
|
||||
|
||||
export const messages = (conversationId: string, messageId?: string) =>
|
||||
`/api/messages/${conversationId}${messageId ? `/${messageId}` : ''}`;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ export function revokeAllUserKeys(): Promise<unknown> {
|
|||
return request.delete(endpoints.revokeAllUserKeys());
|
||||
}
|
||||
|
||||
export function deleteUser(): Promise<s.TPreset> {
|
||||
return request.delete(endpoints.deleteUser());
|
||||
}
|
||||
|
||||
export function getMessagesByConvoId(conversationId: string): Promise<s.TMessage[]> {
|
||||
if (conversationId === 'new') {
|
||||
return Promise.resolve([]);
|
||||
|
|
|
|||
|
|
@ -41,4 +41,5 @@ export enum MutationKeys {
|
|||
assistantAvatarUpload = 'assistantAvatarUpload',
|
||||
updateAction = 'updateAction',
|
||||
deleteAction = 'deleteAction',
|
||||
deleteUser = 'deleteUser',
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue