fixes
This commit is contained in:
parent
b4fe29413b
commit
662f22a58f
|
|
@ -1085,35 +1085,48 @@ const updateBerthRecommendations = async (newRecommendations: number[]) => {
|
|||
// Format date helper function
|
||||
const formatDate = (dateString: string | null | undefined) => {
|
||||
if (!dateString) return "";
|
||||
|
||||
try {
|
||||
let date: Date;
|
||||
|
||||
// Check if it's an ISO date string (e.g., "2025-06-09T22:58:47.731Z")
|
||||
if (dateString.includes('T') || dateString.includes('Z')) {
|
||||
date = new Date(dateString);
|
||||
}
|
||||
// Handle DD-MM-YYYY format
|
||||
if (dateString.includes("-")) {
|
||||
const parts = dateString.split("-");
|
||||
if (parts.length === 3) {
|
||||
const [day, month, year] = parts;
|
||||
const date = new Date(
|
||||
parseInt(year),
|
||||
parseInt(month) - 1,
|
||||
parseInt(day)
|
||||
);
|
||||
return date.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
else if (dateString.match(/^\d{2}-\d{2}-\d{4}$/)) {
|
||||
const [day, month, year] = dateString.split("-");
|
||||
date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
|
||||
}
|
||||
// Handle YYYY-MM-DD format
|
||||
else if (dateString.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
||||
date = new Date(dateString);
|
||||
}
|
||||
// Fallback to direct date parsing
|
||||
const date = new Date(dateString);
|
||||
if (!isNaN(date.getTime())) {
|
||||
return date.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
// Fallback to direct parsing
|
||||
else {
|
||||
date = new Date(dateString);
|
||||
}
|
||||
|
||||
// Check if date is valid
|
||||
if (isNaN(date.getTime())) {
|
||||
return dateString;
|
||||
}
|
||||
|
||||
// Format date in DD/MM/YYYY HH:mm format
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const year = date.getFullYear();
|
||||
const hours = date.getHours().toString().padStart(2, '0');
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
|
||||
// Include time if it's not midnight
|
||||
if (hours !== '00' || minutes !== '00') {
|
||||
return `${day}/${month}/${year} ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
return `${day}/${month}/${year}`;
|
||||
} catch (error) {
|
||||
console.error('Date formatting error:', error, dateString);
|
||||
return dateString;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -162,6 +162,29 @@ NUXT_DOCUMENSO_BASE_URL=https://signatures.portnimara.dev
|
|||
- **Cause**: BCC search criteria not supported by all IMAP servers
|
||||
- **Solution**: Removed BCC from search criteria, now only searches FROM, TO, and CC fields
|
||||
|
||||
### 15. MinIO Private Bucket Authentication Fix
|
||||
- **Problem**: Email caching failed when MinIO buckets were set to private
|
||||
- **Cause**: Frontend was trying to fetch presigned URLs which failed with CORS/auth issues
|
||||
- **Solution**:
|
||||
- Now reading cached emails directly on server using MinIO client
|
||||
- No presigned URLs needed - server has full authentication
|
||||
- Works perfectly with private buckets
|
||||
|
||||
### 16. Email Fetching & Caching Improvements
|
||||
- **Optimizations**:
|
||||
- Date-based IMAP search (last 30 days) to reduce email count
|
||||
- Increased timeout from 15s to 30s
|
||||
- Now caching ALL emails (both sent and received)
|
||||
- Fire-and-forget caching to avoid slowing down email fetch
|
||||
- Proper IMAP date format (e.g., "1-Jan-2024")
|
||||
|
||||
### 17. Date Display Fix
|
||||
- **Problem**: EOI dates showing wrong format/timezone
|
||||
- **Solution**:
|
||||
- Enhanced date parsing to handle ISO dates, DD-MM-YYYY, and YYYY-MM-DD
|
||||
- Now displays in DD/MM/YYYY HH:mm format
|
||||
- Properly handles timezones
|
||||
|
||||
## How It Works Now
|
||||
|
||||
1. **Email Session Management**:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Imap from 'imap';
|
||||
import { simpleParser } from 'mailparser';
|
||||
import { getCredentialsFromSession, decryptCredentials } from '~/server/utils/encryption';
|
||||
import { listFiles, getFileStats } from '~/server/utils/minio';
|
||||
import { listFiles, getFileStats, getMinioClient, uploadFile } from '~/server/utils/minio';
|
||||
|
||||
interface EmailMessage {
|
||||
id: string;
|
||||
|
|
@ -72,12 +72,25 @@ export default defineEventHandler(async (event) => {
|
|||
for (const file of files) {
|
||||
if (file.name.endsWith('.json') && !file.isFolder) {
|
||||
try {
|
||||
// Use the getDownloadUrl function to get a proper presigned URL
|
||||
const { getDownloadUrl } = await import('~/server/utils/minio');
|
||||
const downloadUrl = await getDownloadUrl(file.name);
|
||||
// Read file directly on server using MinIO client (works with private buckets)
|
||||
const client = getMinioClient();
|
||||
const bucketName = useRuntimeConfig().minio.bucketName;
|
||||
|
||||
const response = await fetch(downloadUrl);
|
||||
const emailData = await response.json();
|
||||
// Get object as stream
|
||||
const stream = await client.getObject(bucketName, file.name);
|
||||
|
||||
// Convert stream to string
|
||||
let data = '';
|
||||
stream.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
stream.on('end', () => resolve(data));
|
||||
stream.on('error', reject);
|
||||
});
|
||||
|
||||
const emailData = JSON.parse(data);
|
||||
cachedEmails.push(emailData);
|
||||
} catch (err) {
|
||||
console.error('Failed to read cached email:', file.name, err);
|
||||
|
|
@ -103,15 +116,15 @@ export default defineEventHandler(async (event) => {
|
|||
authTimeout: 5000 // 5 seconds auth timeout
|
||||
};
|
||||
|
||||
// Fetch emails from IMAP with timeout
|
||||
// Fetch emails from IMAP with timeout (increased to 30 seconds)
|
||||
let imapEmails: EmailMessage[] = [];
|
||||
const timeoutPromise = new Promise<EmailMessage[]>((_, reject) =>
|
||||
setTimeout(() => reject(new Error('IMAP connection timeout')), 15000)
|
||||
setTimeout(() => reject(new Error('IMAP connection timeout')), 30000)
|
||||
);
|
||||
|
||||
try {
|
||||
imapEmails = await Promise.race([
|
||||
fetchImapEmails(imapConfig, userEmail, clientEmail, limit),
|
||||
fetchImapEmails(imapConfig, userEmail, clientEmail, limit, interestId),
|
||||
timeoutPromise
|
||||
]);
|
||||
} catch (imapError) {
|
||||
|
|
@ -159,7 +172,8 @@ async function fetchImapEmails(
|
|||
imapConfig: any,
|
||||
userEmail: string,
|
||||
clientEmail: string,
|
||||
limit: number
|
||||
limit: number,
|
||||
interestId?: string
|
||||
): Promise<EmailMessage[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const emails: EmailMessage[] = [];
|
||||
|
|
@ -210,9 +224,16 @@ async function fetchImapEmails(
|
|||
return;
|
||||
}
|
||||
|
||||
// Use ALL to get all messages, then filter manually
|
||||
// This avoids the complex search criteria issues
|
||||
imap.search(['ALL'], (err, results) => {
|
||||
// Use date-based search to reduce the number of emails fetched
|
||||
// Search for emails from the last 30 days
|
||||
const thirtyDaysAgo = new Date();
|
||||
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
||||
|
||||
// Format date for IMAP (e.g., "1-Jan-2024")
|
||||
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
const searchDate = `${thirtyDaysAgo.getDate()}-${months[thirtyDaysAgo.getMonth()]}-${thirtyDaysAgo.getFullYear()}`;
|
||||
|
||||
imap.search(['SINCE', searchDate], (err, results) => {
|
||||
if (err) {
|
||||
console.error(`Search error in ${folderName}:`, err);
|
||||
searchNextFolder();
|
||||
|
|
@ -286,6 +307,27 @@ async function fetchImapEmails(
|
|||
}
|
||||
|
||||
allEmails.push(email);
|
||||
|
||||
// Cache this email if we have an interestId
|
||||
if (interestId && involvesClient) {
|
||||
try {
|
||||
const emailData = {
|
||||
...email,
|
||||
interestId: interestId
|
||||
};
|
||||
|
||||
const objectName = `client-emails/interest-${interestId}/${Date.now()}-${email.direction}.json`;
|
||||
const buffer = Buffer.from(JSON.stringify(emailData, null, 2));
|
||||
|
||||
// Fire and forget - don't wait for upload
|
||||
uploadFile(objectName, buffer, 'application/json').catch(err => {
|
||||
console.error('Failed to cache email:', err);
|
||||
});
|
||||
} catch (cacheError) {
|
||||
console.error('Failed to cache email:', cacheError);
|
||||
}
|
||||
}
|
||||
|
||||
messagesProcessed++;
|
||||
|
||||
if (messagesProcessed === messagesToFetch.length) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue