mirror of
https://github.com/rustmailer/bichon.git
synced 2026-08-03 07:48:34 +02:00
refactor: use UUID for envelope id to prevent accidental deletion
This commit is contained in:
@@ -27,7 +27,7 @@ export interface PaginatedResponse<S> {
|
||||
|
||||
|
||||
export interface EmailEnvelope {
|
||||
id: number;
|
||||
id: string;
|
||||
message_id: string;
|
||||
account_id: number;
|
||||
account_email?: string;
|
||||
@@ -42,7 +42,8 @@ export interface EmailEnvelope {
|
||||
date: number;
|
||||
internal_date: number;
|
||||
size: number;
|
||||
thread_id: number,
|
||||
thread_id: string,
|
||||
attachment_count: number;
|
||||
tags: string[];
|
||||
content_hash: string;
|
||||
}
|
||||
@@ -21,20 +21,7 @@ import { EmailEnvelope, PaginatedResponse } from "@/api";
|
||||
import axiosInstance from "@/api/axiosInstance";
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
// export const list_messages = async (accountId: number, mailbox_id: number, page: number, page_size: number) => {
|
||||
// const params = new URLSearchParams({
|
||||
// mailbox_id: String(mailbox_id),
|
||||
// page: String(page),
|
||||
// page_size: String(page_size),
|
||||
// });
|
||||
|
||||
// const response = await axiosInstance.get<PaginatedResponse<EmailEnvelope>>(
|
||||
// `api/v1/list-messages/${accountId}?${params.toString()}`
|
||||
// );
|
||||
// return response.data;
|
||||
// };
|
||||
|
||||
export const get_thread_messages = async (accountId: number, thread_id: number, page: number, page_size: number) => {
|
||||
export const get_thread_messages = async (accountId: number, thread_id: string, page: number, page_size: number) => {
|
||||
const params = new URLSearchParams({
|
||||
thread_id: String(thread_id),
|
||||
page: String(page),
|
||||
@@ -47,13 +34,13 @@ export const get_thread_messages = async (accountId: number, thread_id: number,
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const download_attachment = async (accountId: number, id: number, attachmentFileName: string) => {
|
||||
export const download_attachment = async (accountId: number, id: string, attachmentFileName: string) => {
|
||||
const response = await axiosInstance.get(`api/v1/download-attachment/${accountId}/${id}?name=${attachmentFileName}`, { responseType: 'blob' });
|
||||
const blob = new Blob([response.data]);
|
||||
saveAs(blob, attachmentFileName);
|
||||
};
|
||||
|
||||
export const download_nested_attachment = async (accountId: number, id: number, attachmentFileName: string, nestedAttachmentFileName: string) => {
|
||||
export const download_nested_attachment = async (accountId: number, id: string, attachmentFileName: string, nestedAttachmentFileName: string) => {
|
||||
const response = await axiosInstance.get(`api/v1/download-nested-attachment/${accountId}/${id}?name=${attachmentFileName}&nested_name=${nestedAttachmentFileName}`, { responseType: 'blob' });
|
||||
const blob = new Blob([response.data]);
|
||||
saveAs(blob, nestedAttachmentFileName);
|
||||
@@ -92,30 +79,30 @@ export const getContent = (messageContent: MessageContentResponse): string | nul
|
||||
return null;
|
||||
};
|
||||
|
||||
export const load_message = async (accountId: number, id: number) => {
|
||||
export const load_message = async (accountId: number, id: string) => {
|
||||
const response = await axiosInstance.get<MessageContentResponse>(`api/v1/message-content/${accountId}/${id}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const load_nested_message = async (accountId: number, id: number, attachmentFileName: string) => {
|
||||
export const load_nested_message = async (accountId: number, id: string, attachmentFileName: string) => {
|
||||
const response = await axiosInstance.get<NestedMessageContentResponse>(`api/v1/nested-message-content/${accountId}/${id}?name=${attachmentFileName}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const delete_messages = async (payload: Record<string, number[]>) => {
|
||||
export const delete_messages = async (payload: Record<number, string[]>) => {
|
||||
const response = await axiosInstance.post("api/v1/delete-messages", payload);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const download_message = async (accountId: number, id: number) => {
|
||||
export const download_message = async (accountId: number, id: string) => {
|
||||
const response = await axiosInstance.get(`api/v1/download-message/${accountId}/${id}`, { responseType: 'blob' });
|
||||
const blob = new Blob([response.data]);
|
||||
saveAs(blob, `${id}.eml`);
|
||||
};
|
||||
|
||||
export const restore_message = async (accountId: number, messageIds: number[]) => {
|
||||
export const restore_message = async (accountId: number, envelopeIds: string[]) => {
|
||||
const response = await axiosInstance.post(`api/v1/restore-messages/${accountId}`, {
|
||||
message_ids: messageIds,
|
||||
envelope_ids: envelopeIds,
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
@@ -28,10 +28,10 @@ interface SearchContextType {
|
||||
setOpen: (str: SearchDialogType | null) => void
|
||||
currentEnvelope: EmailEnvelope | undefined
|
||||
setCurrentEnvelope: React.Dispatch<React.SetStateAction<EmailEnvelope | undefined>>
|
||||
toDelete: Map<number, Set<number>>
|
||||
setToDelete: React.Dispatch<React.SetStateAction<Map<number, Set<number>>>>
|
||||
selected: Map<number, Set<number>>
|
||||
setSelected: React.Dispatch<React.SetStateAction<Map<number, Set<number>>>>
|
||||
toDelete: Map<number, Set<string>>
|
||||
setToDelete: React.Dispatch<React.SetStateAction<Map<number, Set<string>>>>
|
||||
selected: Map<number, Set<string>>
|
||||
setSelected: React.Dispatch<React.SetStateAction<Map<number, Set<string>>>>
|
||||
deleteMailboxId: string | undefined
|
||||
setDeleteMailboxId: React.Dispatch<React.SetStateAction<string | undefined>>
|
||||
selectedAccountId: number | undefined
|
||||
|
||||
@@ -37,7 +37,7 @@ export function EnvelopeDeleteDialog({ open, onOpenChange }: Props) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: ({ payload }: { payload: Record<string, number[]> }) =>
|
||||
mutationFn: ({ payload }: { payload: Record<number, string[]> }) =>
|
||||
delete_messages(payload),
|
||||
retry: false,
|
||||
onSuccess: () => {
|
||||
|
||||
@@ -39,8 +39,8 @@ export default function Search() {
|
||||
const { t } = useTranslation()
|
||||
const [selectedEnvelope, setSelectedEnvelope] = React.useState<EmailEnvelope | undefined>(undefined);
|
||||
const [open, setOpen] = useDialogState<SearchDialogType>(null)
|
||||
const [toDelete, setToDelete] = React.useState<Map<number, Set<number>>>(new Map());
|
||||
const [selected, setSelected] = React.useState<Map<number, Set<number>>>(new Map());
|
||||
const [toDelete, setToDelete] = React.useState<Map<number, Set<string>>>(new Map());
|
||||
const [selected, setSelected] = React.useState<Map<number, Set<string>>>(new Map());
|
||||
const [selectedTags, setSelectedTags] = React.useState<string[]>([]);
|
||||
const [sorting, setSorting] = React.useState<SortingState>([{ id: "date", desc: true }]);
|
||||
const [deleteMailboxId, setDeleteMailboxId] = React.useState<string | undefined>(undefined);
|
||||
|
||||
@@ -273,7 +273,7 @@ export function MailListTable({
|
||||
}
|
||||
}
|
||||
|
||||
const toggleSelected = (accountId: number, mailId: number) => {
|
||||
const toggleSelected = (accountId: number, mailId: string) => {
|
||||
setSelected(prev => {
|
||||
const next = new Map(prev)
|
||||
const set = new Set(next.get(accountId) || [])
|
||||
@@ -292,7 +292,7 @@ export function MailListTable({
|
||||
|
||||
const totalSelected = Array.from(selected.values()).reduce((sum, set) => sum + set.size, 0)
|
||||
|
||||
const hasSelected = (accountId: number, mailId: number) => selected.get(accountId)?.has(mailId) ?? false
|
||||
const hasSelected = (accountId: number, mailId: string) => selected.get(accountId)?.has(mailId) ?? false
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
|
||||
@@ -66,7 +66,7 @@ export function MailList({
|
||||
}
|
||||
}
|
||||
|
||||
const toggleToDelete = (accountId: number, mailId: number) => {
|
||||
const toggleToDelete = (accountId: number, mailId: string) => {
|
||||
setToDelete(prev => {
|
||||
const next = new Map(prev);
|
||||
const set = new Set(next.get(accountId) || []);
|
||||
@@ -84,7 +84,7 @@ export function MailList({
|
||||
});
|
||||
};
|
||||
|
||||
const toggleSelected = (accountId: number, mailId: number) => {
|
||||
const toggleSelected = (accountId: number, mailId: string) => {
|
||||
setSelected(prev => {
|
||||
const next = new Map(prev);
|
||||
const set = new Set(next.get(accountId) || []);
|
||||
@@ -105,7 +105,7 @@ export function MailList({
|
||||
const totalSelected = Array.from(selected.values())
|
||||
.reduce((sum, set) => sum + set.size, 0);
|
||||
|
||||
const hasSelected = (accountId: number, mailId: number) => {
|
||||
const hasSelected = (accountId: number, mailId: string) => {
|
||||
return selected.get(accountId)?.has(mailId) ?? false;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ import { NestedEmailDialog } from './nested-email-dialog';
|
||||
|
||||
interface MailMessageViewProps {
|
||||
envelope: {
|
||||
id: number;
|
||||
id: string;
|
||||
account_id: number,
|
||||
from?: string;
|
||||
to?: string[];
|
||||
@@ -172,7 +172,7 @@ export function MailMessageView({
|
||||
setNestedEmlFile(filename);
|
||||
};
|
||||
|
||||
const toggleToDelete = (accountId: number, mailId: number) => {
|
||||
const toggleToDelete = (accountId: number, mailId: string) => {
|
||||
setToDelete(prev => {
|
||||
const next = new Map(prev);
|
||||
const set = new Set(next.get(accountId) || []);
|
||||
|
||||
@@ -40,7 +40,7 @@ export function DataTableRowActions({ row }: DataTableRowActionsProps) {
|
||||
const { setOpen, setCurrentEnvelope, setSelected, setToDelete } = useSearchContext()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const toggleToDelete = (accountId: number, mailId: number) => {
|
||||
const toggleToDelete = (accountId: number, mailId: string) => {
|
||||
setToDelete(prev => {
|
||||
const next = new Map(prev)
|
||||
const set = new Set(next.get(accountId) || [])
|
||||
|
||||
@@ -42,7 +42,7 @@ interface MailThreadDialogProps {
|
||||
|
||||
export function MailThreadDialog({ open, onOpenChange }: MailThreadDialogProps) {
|
||||
const { currentEnvelope } = useSearchContext();
|
||||
const [expandedIds, setExpandedIds] = useState<Set<number>>(new Set());
|
||||
const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());
|
||||
const { t } = useTranslation();
|
||||
|
||||
const threadId = currentEnvelope?.thread_id;
|
||||
@@ -73,7 +73,7 @@ export function MailThreadDialog({ open, onOpenChange }: MailThreadDialogProps)
|
||||
const allMessages = data?.pages.flatMap((page) => page.items) ?? [];
|
||||
const totalCount = data?.pages[0]?.total_items ?? 0;
|
||||
|
||||
const toggleExpand = (id: number) => {
|
||||
const toggleExpand = (id: string) => {
|
||||
setExpandedIds((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.has(id) ? next.delete(id) : next.add(id);
|
||||
|
||||
@@ -22,7 +22,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from './use-toast';
|
||||
|
||||
export interface UpdateTagsParams {
|
||||
updates: Record<number, number[]>;
|
||||
updates: Record<number, string[]>;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
|
||||
@@ -63,10 +63,10 @@ export const validateFlag = (input: string): string | null => {
|
||||
|
||||
|
||||
export function mapToRecordOfArrays(
|
||||
map: Map<number, Set<number>>
|
||||
): Record<string, number[]> {
|
||||
map: Map<number, Set<string>>
|
||||
): Record<number, string[]> {
|
||||
return Object.fromEntries(
|
||||
Array.from(map.entries()).map(([key, value]) => [String(key), Array.from(value)])
|
||||
Array.from(map.entries()).map(([key, value]) => [key, Array.from(value)])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user