import { Router, Request, Response, NextFunction } from 'express'; import { z } from 'zod'; import { AppError } from '../middleware/errorHandler'; import { buildGoogleAuthUrl, exchangeGoogleAuthCode, getGoogleOAuthSettings, getPostmasterStatus, } from '../services/postmaster.service'; import { buildMicrosoftAuthUrl, exchangeMicrosoftAuthCode, getMicrosoftOAuthSettings, } from '../services/microsoft-oauth.service'; import { upsertSetting } from '../services/settings.service'; const router = Router(); const googleSettingsSchema = z.object({ google_oauth_client_id: z.string().min(1), google_oauth_client_secret: z.string().min(1), }); router.get('/google/status', async (_req: Request, res: Response, next: NextFunction) => { try { const status = await getPostmasterStatus(); const { clientId } = await getGoogleOAuthSettings(); res.json({ ...status, hasClientId: Boolean(clientId), redirectUri: undefined, }); } catch (err) { next(err); } }); router.post('/google/credentials', async (req: Request, res: Response, next: NextFunction) => { try { const data = googleSettingsSchema.parse(req.body); await upsertSetting('google_oauth_client_id', data.google_oauth_client_id); await upsertSetting('google_oauth_client_secret', data.google_oauth_client_secret); const status = await getPostmasterStatus(); res.json(status); } catch (err) { next(err); } }); router.get('/google/auth-url', async (_req: Request, res: Response, next: NextFunction) => { try { const { clientId } = await getGoogleOAuthSettings(); if (!clientId) { throw new AppError(400, 'Configure Google OAuth client ID and secret first'); } res.json({ url: buildGoogleAuthUrl(clientId) }); } catch (err) { next(err); } }); const microsoftSettingsSchema = z.object({ microsoft_oauth_client_id: z.string().min(1), microsoft_oauth_client_secret: z.string().min(1), }); router.get('/microsoft/status', async (_req: Request, res: Response, next: NextFunction) => { try { const { clientId, clientSecret } = await getMicrosoftOAuthSettings(); res.json({ configured: Boolean(clientId && clientSecret), hasClientId: Boolean(clientId), }); } catch (err) { next(err); } }); router.post('/microsoft/credentials', async (req: Request, res: Response, next: NextFunction) => { try { const data = microsoftSettingsSchema.parse(req.body); await upsertSetting('microsoft_oauth_client_id', data.microsoft_oauth_client_id); await upsertSetting('microsoft_oauth_client_secret', data.microsoft_oauth_client_secret); res.json({ configured: true }); } catch (err) { next(err); } }); router.get('/microsoft/auth-url', async (req: Request, res: Response, next: NextFunction) => { try { const { clientId } = await getMicrosoftOAuthSettings(); if (!clientId) { throw new AppError(400, 'Configure Microsoft OAuth client ID and secret first'); } const state = typeof req.query.state === 'string' ? req.query.state : 'mailbox'; res.json({ url: buildMicrosoftAuthUrl(clientId, state) }); } catch (err) { next(err); } }); export async function microsoftOAuthCallback(req: Request, res: Response): Promise { try { const code = typeof req.query.code === 'string' ? req.query.code : null; if (!code) { res.status(400).send('Missing authorization code'); return; } const { refreshToken } = await exchangeMicrosoftAuthCode(code); res.send(`

Microsoft account connected

You can close this tab and return to Warmbox.

`); } catch (err) { const message = err instanceof Error ? err.message : 'OAuth callback failed'; res.status(500).send(message); } } export async function googleOAuthCallback(req: Request, res: Response): Promise { try { const code = typeof req.query.code === 'string' ? req.query.code : null; if (!code) { res.status(400).send('Missing authorization code'); return; } const refreshToken = await exchangeGoogleAuthCode(code); await upsertSetting('google_postmaster_refresh_token', refreshToken); res.send(`

Google Postmaster connected

Warmbox can now verify your domains against Postmaster Tools.

You can close this tab and return to the dashboard Settings page.

`); } catch (err) { const message = err instanceof Error ? err.message : 'OAuth callback failed'; res.status(500).send(message); } } export default router;