sync/src/web/routes/api/integrations-oauth.js
2026-05-31 23:56:37 +02:00

59 lines
2.4 KiB
JavaScript

const express = require('express');
const webserver = require('../../webserver');
const Config = require('../../../config');
const calendarDB = require('../../../database/calendar-integrations');
const googleCalendar = require('../../../integrations/google-calendar');
const { getChannelRow, getUserEffectiveRank } = require('./middleware');
const router = express.Router();
router.get('/google/callback', async (req, res) => {
const user = await webserver.authorize(req);
if (!user) return res.status(401).send('Unauthorized');
const code = String(req.query.code || '').trim();
const state = String(req.query.state || '').trim();
if (!code || !state) return res.status(400).send('Missing code/state');
let decoded;
try {
decoded = googleCalendar.decodeState(state, Config.get('http.cookie-secret'));
} catch (err) {
return res.status(400).send(err.message || 'Invalid state');
}
if (!decoded.channel || !decoded.calendarId) {
return res.status(400).send('State is missing channel/calendar info');
}
if (String(decoded.actor || '').toLowerCase() !== String(user.name || '').toLowerCase()) {
return res.status(403).send('OAuth callback user mismatch');
}
const channelRow = await getChannelRow(decoded.channel).catch(() => null);
if (!channelRow) return res.status(404).send('Channel not found');
const rank = await getUserEffectiveRank(user, channelRow);
if (rank < 3) return res.status(403).send('Insufficient rank');
try {
const tokenPayload = await googleCalendar.exchangeCodeForToken(code);
const packed = googleCalendar.packTokens(tokenPayload);
await calendarDB.upsertGoogleIntegration(channelRow.id, {
status: 'connected',
config: {
calendar_id: decoded.calendarId,
sync_statuses: ['scheduled', 'running', 'paused', 'completed']
},
token_encrypted: packed.token_encrypted,
refresh_token_encrypted: packed.refresh_token_encrypted,
token_expires_at: packed.token_expires_at,
connected_by: user.name,
updated_by: user.name
});
res.send(`Google Calendar connected for channel ${channelRow.name}. You can close this tab.`);
} catch (err) {
res.status(400).send(err.message || 'OAuth exchange failed');
}
});
module.exports = router;