/** * OAuth Server for VC-SYS CLI - TypeScript port * Handles local callback server for OAuth flow */ import express, { Request, Response } from 'express'; import { createServer, Server } from 'http'; import open from 'open'; import chalk from 'chalk'; import { Logger } from '../core/logger'; export interface OAuthCallbackData { code?: string; state?: string; error?: string; } export class OAuthServer { private app: express.Application; private server: Server | null = null; private port: number = 8080; private logger: Logger; constructor() { this.logger = new Logger('OAuthServer'); this.app = express(); } /** * Start temporary server for OAuth callback */ async startServer(): Promise { return new Promise((resolve, reject) => { // Set up callback route this.app.get('/callback', (req: Request, res: Response) => { // Send success page to user res.send(this.getSuccessPage()); // Resolve with query parameters const callbackData: OAuthCallbackData = { code: req.query.code as string, state: req.query.state as string, error: req.query.error as string }; this.logger.info('OAuth callback received', { hasCode: !!callbackData.code, hasState: !!callbackData.state, hasError: !!callbackData.error }); resolve(callbackData); // Close server after successful callback setTimeout(() => this.stopServer(), 1000); }); // Health check endpoint this.app.get('/health', (req: Request, res: Response) => { res.json({ status: 'ok', service: 'vcsys-oauth-server' }); }); // Start server with automatic port resolution this.startServerWithPortFallback(resolve, reject); }); } /** * Start server with automatic port fallback */ private startServerWithPortFallback( resolve: (value: OAuthCallbackData) => void, reject: (reason?: any) => void ): void { this.server = createServer(this.app); this.server.listen(this.port, () => { this.logger.info('OAuth callback server started', { port: this.port, callbackUrl: this.getRedirectUri() }); console.log(chalk.dim(`🔐 OAuth callback server running on ${this.getRedirectUri()}`)); }); // Handle server errors with port fallback this.server.on('error', (error: any) => { if (error.code === 'EADDRINUSE' && this.port < 8090) { this.logger.debug('Port in use, trying next port', { port: this.port }); this.port = this.port + 1; this.server?.close(); this.startServerWithPortFallback(resolve, reject); } else { this.logger.error('OAuth server failed to start', error); reject(new Error(`Failed to start OAuth server: ${error.message}`)); } }); // Handle server close this.server.on('close', () => { this.logger.debug('OAuth server closed'); }); } /** * Stop the server */ stopServer(): void { if (this.server) { this.server.close((error) => { if (error) { this.logger.error('Error stopping OAuth server', error); } else { this.logger.info('OAuth callback server stopped'); console.log(chalk.dim('✅ OAuth callback server stopped')); } }); this.server = null; } } /** * Get current redirect URI */ getRedirectUri(): string { return `http://localhost:${this.port}/callback`; } /** * Get current port */ getPort(): number { return this.port; } /** * HTML success page shown to user after OAuth */ private getSuccessPage(): string { return ` VC-SYS CLI - Authentication Successful

Authentication Successful!

Your Supabase account has been successfully connected to VC-SYS CLI.

You can now close this browser tab and return to your terminal to continue.

Next Steps:
$ vcsys auth status
Check your authentication status
$ vcsys provision --interactive
Create your first Supabase project
`; } /** * Force cleanup on process exit */ cleanup(): void { this.stopServer(); } }