rspace-online/server/oauth/index.ts

86 lines
2.6 KiB
TypeScript

/**
* OAuth route mounting for external integrations.
*
* Provides OAuth2 authorize/callback/disconnect flows for:
* - Notion (workspace-level integration)
* - Google (user-level, with token refresh)
* - ClickUp (workspace-level, task sync)
*
* Also provides GET /status?space=X to check connection status (no tokens).
*
* Tokens are stored in Automerge docs per space via SyncServer.
*/
import { Hono } from 'hono';
import { notionOAuthRoutes } from './notion';
import { googleOAuthRoutes } from './google';
import { clickupOAuthRoutes } from './clickup';
import { connectionsDocId } from '../../modules/rnotes/schemas';
import { clickupConnectionDocId } from '../../modules/rtasks/schemas';
import type { ConnectionsDoc } from '../../modules/rnotes/schemas';
import type { ClickUpConnectionDoc } from '../../modules/rtasks/schemas';
import type { SyncServer } from '../local-first/sync-server';
const oauthRouter = new Hono();
let _syncServer: SyncServer | null = null;
export function setOAuthStatusSyncServer(ss: SyncServer) {
_syncServer = ss;
}
oauthRouter.route('/notion', notionOAuthRoutes);
oauthRouter.route('/google', googleOAuthRoutes);
oauthRouter.route('/clickup', clickupOAuthRoutes);
// GET /status?space=X — return connection status for all providers (no tokens)
oauthRouter.get('/status', (c) => {
const space = c.req.query('space');
if (!space) return c.json({ error: 'space query param required' }, 400);
if (!_syncServer) return c.json({ error: 'SyncServer not initialized' }, 500);
// Read rNotes connections doc (Google + Notion)
const connDoc = _syncServer.getDoc<ConnectionsDoc>(connectionsDocId(space));
// Read rTasks ClickUp connection doc
const clickupDoc = _syncServer.getDoc<ClickUpConnectionDoc>(clickupConnectionDocId(space));
const status: Record<string, { connected: boolean; connectedAt?: number; email?: string; workspaceName?: string; teamName?: string }> = {};
// Google
if (connDoc?.google) {
status.google = {
connected: true,
connectedAt: connDoc.google.connectedAt,
email: connDoc.google.email,
};
} else {
status.google = { connected: false };
}
// Notion
if (connDoc?.notion) {
status.notion = {
connected: true,
connectedAt: connDoc.notion.connectedAt,
workspaceName: connDoc.notion.workspaceName,
};
} else {
status.notion = { connected: false };
}
// ClickUp
if (clickupDoc?.clickup) {
status.clickup = {
connected: true,
connectedAt: clickupDoc.clickup.connectedAt,
teamName: clickupDoc.clickup.teamName,
};
} else {
status.clickup = { connected: false };
}
return c.json(status);
});
export { oauthRouter };