diff --git a/.env b/.env index ce8e91a..dc7dd78 100644 --- a/.env +++ b/.env @@ -67,6 +67,6 @@ DB_NAME=rivacube # IMAP Configuration IMAP_USER=alma@governance-labs.org -IMAP_PASSWORD=your_imap_password_here +IMAP_PASSWORD=8s-hN8u37-IP#-y IMAP_HOST=mail.infomaniak.com IMAP_PORT=993 diff --git a/app/api/mail/route.ts b/app/api/mail/route.ts index a0323dc..be42cf7 100644 --- a/app/api/mail/route.ts +++ b/app/api/mail/route.ts @@ -11,42 +11,58 @@ console.log('Environment Variables:', { NODE_ENV: process.env.NODE_ENV }); +// Helper function to get stored credentials +function getStoredCredentials() { + if (typeof window === 'undefined') { + // Server-side: use environment variables + return { + user: process.env.IMAP_USER, + password: process.env.IMAP_PASSWORD, + host: process.env.IMAP_HOST, + port: process.env.IMAP_PORT + }; + } + + // Client-side: use localStorage + const stored = localStorage.getItem('imapCredentials'); + return stored ? JSON.parse(stored) : null; +} + // IMAP configuration -const imapConfig: Imap.Config = { - user: process.env.IMAP_USER || 'alma@governance-labs.org', - password: process.env.IMAP_PASSWORD || '', - host: process.env.IMAP_HOST || 'mail.infomaniak.com', - port: parseInt(process.env.IMAP_PORT || '993', 10), - tls: true, - tlsOptions: { - rejectUnauthorized: false, - servername: process.env.IMAP_HOST || 'mail.infomaniak.com' - }, - authTimeout: 10000, - connTimeout: 10000, - debug: console.log // Enable IMAP debug logging +const getImapConfig = () => { + const credentials = getStoredCredentials(); + + if (!credentials?.user || !credentials?.password) { + throw new Error('Email credentials not found. Please log in first.'); + } + + return { + user: credentials.user, + password: credentials.password, + host: credentials.host || 'mail.infomaniak.com', + port: parseInt(credentials.port || '993', 10), + tls: true, + tlsOptions: { + rejectUnauthorized: false, + servername: credentials.host || 'mail.infomaniak.com' + }, + authTimeout: 10000, + connTimeout: 10000, + debug: console.log + }; }; // Debug logging for IMAP configuration console.log('IMAP Configuration:', { - user: imapConfig.user, - host: imapConfig.host, - port: imapConfig.port, - tls: imapConfig.tls, - hasPassword: !!imapConfig.password, - authTimeout: imapConfig.authTimeout, - connTimeout: imapConfig.connTimeout + user: getImapConfig().user, + host: getImapConfig().host, + port: getImapConfig().port, + tls: getImapConfig().tls, + hasPassword: !!getImapConfig().password, + authTimeout: getImapConfig().authTimeout, + connTimeout: getImapConfig().connTimeout }); -// Validate IMAP configuration -if (!imapConfig.user || !imapConfig.password) { - console.error('IMAP configuration error:', { - user: imapConfig.user, - hasPassword: !!imapConfig.password - }); - throw new Error('IMAP credentials are not properly configured. Please check your .env file.'); -} - interface ImapMessage { header: { from?: string[]; @@ -68,7 +84,7 @@ interface ImapError extends Error { } // Helper function to create a promise-based IMAP connection -function createImapConnection() { +function createImapConnection(imapConfig: Imap.Config) { return new Promise((resolve, reject) => { console.log('Creating new IMAP connection with config:', { user: imapConfig.user, @@ -179,17 +195,17 @@ export async function GET(request: Request) { try { console.log('Starting email fetch process...'); - // Validate IMAP configuration - if (!imapConfig.user || !imapConfig.password) { - console.error('IMAP configuration error:', { - user: imapConfig.user, - hasPassword: !!imapConfig.password - }); - throw new Error('IMAP credentials are not properly configured. Please check your .env file.'); - } + const imapConfig = getImapConfig(); + console.log('IMAP Configuration:', { + user: imapConfig.user, + host: imapConfig.host, + port: imapConfig.port, + tls: imapConfig.tls, + hasPassword: !!imapConfig.password + }); console.log('Creating IMAP connection...'); - const imap = await createImapConnection() as Imap; + const imap = await createImapConnection(imapConfig) as Imap; console.log('Fetching messages...'); const messages = await fetchMessages(imap, 'INBOX'); @@ -226,7 +242,7 @@ export async function GET(request: Request) { // Add endpoint to get mailboxes export async function POST(request: Request) { try { - const imap = await createImapConnection() as Imap; + const imap = await createImapConnection(getImapConfig()) as Imap; const mailboxes = await new Promise((resolve, reject) => { imap.getBoxes((err, boxes) => { diff --git a/app/api/mail/test-connection/route.ts b/app/api/mail/test-connection/route.ts new file mode 100644 index 0000000..99bb7f6 --- /dev/null +++ b/app/api/mail/test-connection/route.ts @@ -0,0 +1,50 @@ +import { NextResponse } from 'next/server'; +import Imap from 'imap'; + +export async function POST(request: Request) { + try { + const { email, password, host, port } = await request.json(); + + // IMAP configuration + const imapConfig: Imap.Config = { + user: email, + password: password, + host: host, + port: parseInt(port, 10), + tls: true, + tlsOptions: { + rejectUnauthorized: false, + servername: host + }, + authTimeout: 10000, + connTimeout: 10000 + }; + + // Create a promise-based IMAP connection + const imap = new Imap(imapConfig); + + return new Promise((resolve, reject) => { + imap.once('ready', () => { + imap.end(); + resolve(NextResponse.json({ success: true })); + }); + + imap.once('error', (err: Error) => { + console.error('IMAP connection error:', err); + reject(NextResponse.json({ + error: 'Failed to connect to email server', + details: err.message + }, { status: 401 })); + }); + + imap.connect(); + }); + + } catch (error) { + console.error('Error in test connection:', error); + return NextResponse.json({ + error: 'Failed to test connection', + details: error instanceof Error ? error.message : 'Unknown error' + }, { status: 500 }); + } +} \ No newline at end of file diff --git a/app/mail/login/page.tsx b/app/mail/login/page.tsx new file mode 100644 index 0000000..18e269b --- /dev/null +++ b/app/mail/login/page.tsx @@ -0,0 +1,112 @@ +'use client'; + +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { Label } from '@/components/ui/label'; +import { toast } from 'sonner'; + +export default function EmailLoginPage() { + const router = useRouter(); + const [loading, setLoading] = useState(false); + const [credentials, setCredentials] = useState({ + email: '', + password: '', + host: 'mail.infomaniak.com', + port: '993' + }); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + + try { + // Store credentials in localStorage + localStorage.setItem('imapCredentials', JSON.stringify(credentials)); + + // Test the connection + const response = await fetch('/api/mail/test-connection', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(credentials), + }); + + if (!response.ok) { + throw new Error('Failed to connect to email server'); + } + + toast.success('Successfully connected to email server'); + router.push('/mail'); + } catch (error) { + console.error('Login error:', error); + toast.error('Failed to connect to email server. Please check your credentials.'); + } finally { + setLoading(false); + } + }; + + return ( +
+ + + Email Login + + Enter your email credentials to access your mailbox + + + +
+
+ + setCredentials({ ...credentials, email: e.target.value })} + required + /> +
+
+ + setCredentials({ ...credentials, password: e.target.value })} + required + /> +
+
+ + setCredentials({ ...credentials, host: e.target.value })} + required + /> +
+
+ + setCredentials({ ...credentials, port: e.target.value })} + required + /> +
+ +
+
+
+
+ ); +} \ No newline at end of file diff --git a/app/mail/page.tsx b/app/mail/page.tsx index 535ac68..22627c1 100644 --- a/app/mail/page.tsx +++ b/app/mail/page.tsx @@ -19,6 +19,7 @@ import { import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Label } from "@/components/ui/label"; import { MoreVertical, Settings, Plus as PlusIcon, Trash2, Edit, Mail, Inbox, Send, Star, Trash, Plus, ChevronLeft, ChevronRight, Search, ChevronDown, Folder, ChevronUp, Reply, Forward, ReplyAll, MoreHorizontal, FolderOpen, X, Paperclip, MessageSquare } from 'lucide-react'; +import { useRouter } from 'next/navigation'; interface Account { id: number; @@ -386,6 +387,30 @@ function decodeMimeContent(content: string): string { } export default function MailPage() { + const router = useRouter(); + const [loading, setLoading] = useState(true); + + useEffect(() => { + // Check for stored credentials + const storedCredentials = localStorage.getItem('imapCredentials'); + if (!storedCredentials) { + router.push('/mail/login'); + } else { + setLoading(false); + } + }, [router]); + + if (loading) { + return ( +
+
+
+

Loading...

+
+
+ ); + } + // Single IMAP account for now const [accounts, setAccounts] = useState([ { id: 1, name: 'Mail', email: 'alma@governance-labs.org', color: 'bg-blue-500' }, @@ -411,7 +436,6 @@ export default function MailPage() { const [showCc, setShowCc] = useState(false); const [showBcc, setShowBcc] = useState(false); const [emails, setEmails] = useState([]); - const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [composeSubject, setComposeSubject] = useState(''); const [composeRecipient, setComposeRecipient] = useState('');