import { Router } from 'express'; import { buildReceivedDateFilter, fetchGraph, postGraph } from '../utils/index.ts'; import { acquireAppToken } from '../helper/msal.ts'; import { AuthManager } from '../auth/index.ts'; export default (auth?: AuthManager) => { const router = Router(); // Read emails with optional date range router.get('/', async (req, res) => { try { let token: string; try { token = await acquireAppToken(req, auth); } catch (err: any) { return res.status(401).json({ error: 'Failed to acquire access token', message: err?.message ?? String(err) }); } // Support search and from filters in addition to time-based filters const fromFilter = req.query.from as string | undefined; const searchQuery = req.query.search as string | undefined; let endpoint = ''; let extraHeaders = {} as Record; if (searchQuery) { // Graph $search requires ConsistencyLevel: eventual endpoint = `/me/messages?$search=${encodeURIComponent(`"${searchQuery}"`)}`; extraHeaders = { 'ConsistencyLevel': 'eventual' }; } else if (fromFilter) { // filter by sender email address const safe = fromFilter.replace("'", "''"); endpoint = `/me/messages?$filter=(from/emailAddress/address) eq '${safe}'`; } else { // Normalize time-related query params (accept different casings) const timeRange = (req.query.timeRange ?? req.query.timerange) as string | undefined; const start = (req.query.startDateTime ?? req.query.startdatetime ?? req.query.start) as string | undefined; const end = (req.query.endDateTime ?? req.query.enddatetime ?? req.query.end) as string | undefined; try { const filterQuery = buildReceivedDateFilter({ timeRange, startDateTime: start, endDateTime: end, }); endpoint = `/me/messages${filterQuery}`; } catch (err: any) { return res.status(400).json({ error: 'invalid_time_range', message: err.message }); } } const data = await fetchGraph(endpoint, token, extraHeaders); res.json(data); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Create a draft email router.post('/draft', async (req, res) => { try { let token: string; try { token = await acquireAppToken(req, auth); } catch (err: any) { return res.status(401).json({ error: 'Failed to acquire access token', message: err?.message ?? String(err) }); } const { subject, body, toRecipients } = req.body; const messagePayload = { subject: subject || '', body: { contentType: 'HTML', content: body || '' }, toRecipients: toRecipients ? toRecipients.map((email: string) => ({ emailAddress: { address: email } })) : [] }; const data = await postGraph('/me/messages', token, messagePayload); res.json(data); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Reply to an email (draft) router.post('/:id/reply', async (req, res) => { try { let token: string; try { token = await acquireAppToken(req, auth); } catch (err: any) { return res.status(401).json({ error: 'Failed to acquire access token', message: err?.message ?? String(err) }); } const messageId = req.params.id; const { comment } = req.body; const payload = { comment: comment || '' }; // Create a reply draft const data = await postGraph(`/me/messages/${messageId}/createReply`, token, payload); res.json(data); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Send an email immediately router.post('/send', async (req, res) => { try { let token: string; try { token = await acquireAppToken(req, auth); } catch (err: any) { return res.status(401).json({ error: 'Failed to acquire access token', message: err?.message ?? String(err) }); } const { subject, body, toRecipients, contentType } = req.body; if (!subject || !body || !Array.isArray(toRecipients) || toRecipients.length === 0) { return res.status(400).json({ error: 'invalid_payload', message: 'subject, body and toRecipients (non-empty array) are required', }); } const payload = { message: { subject, body: { contentType: contentType === 'Text' ? 'Text' : 'HTML', content: body, }, toRecipients: toRecipients.map((email: string) => ({ emailAddress: { address: email }, })), }, saveToSentItems: true, }; await postGraph('/me/sendMail', token, payload); res.json({ success: true, message: 'Email sent successfully' }); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Reply and send immediately router.post('/:id/reply/send', async (req, res) => { try { let token: string; try { token = await acquireAppToken(req, auth); } catch (err: any) { return res.status(401).json({ error: 'Failed to acquire access token', message: err?.message ?? String(err) }); } const messageId = req.params.id; const { comment } = req.body; await postGraph(`/me/messages/${messageId}/reply`, token, { comment: comment || '' }); res.json({ success: true, message: 'Reply sent successfully' }); } catch (err: any) { res.status(500).json({ error: err.message }); } }); // Forward a message router.post('/:id/forward', async (req, res) => { try { let token: string; try { token = await acquireAppToken(req, auth); } catch (err: any) { return res.status(401).json({ error: 'Failed to acquire access token', message: err?.message ?? String(err) }); } const messageId = req.params.id; const { comment, toRecipients } = req.body; if (!Array.isArray(toRecipients) || toRecipients.length === 0) { return res.status(400).json({ error: 'invalid_payload', message: 'toRecipients (non-empty array) is required' }); } const payload = { comment: comment || '', toRecipients: toRecipients.map((email: string) => ({ emailAddress: { address: email } })), }; await postGraph(`/me/messages/${messageId}/forward`, token, payload); res.json({ success: true, message: 'Message forwarded successfully' }); } catch (err: any) { res.status(500).json({ error: err.message }); } }); return router; };