warmbox/apps/api/src/routes/integrations.routes.ts

159 lines
4.9 KiB
TypeScript

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<void> {
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(`
<!DOCTYPE html>
<html><body style="font-family: sans-serif; padding: 2rem;">
<h1>Microsoft account connected</h1>
<p>You can close this tab and return to Warmbox.</p>
<script>
if (window.opener) {
window.opener.postMessage({
type: 'warmbox-microsoft-oauth',
refreshToken: ${JSON.stringify(refreshToken)}
}, '*');
}
</script>
</body></html>
`);
} 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<void> {
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(`
<!DOCTYPE html>
<html><body style="font-family: sans-serif; padding: 2rem;">
<h1>Google Postmaster connected</h1>
<p>Warmbox can now verify your domains against Postmaster Tools.</p>
<p>You can close this tab and return to the dashboard Settings page.</p>
</body></html>
`);
} catch (err) {
const message = err instanceof Error ? err.message : 'OAuth callback failed';
res.status(500).send(message);
}
}
export default router;