159 lines
4.9 KiB
TypeScript
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;
|