()
| 53 | } |
| 54 | |
| 55 | async function main() { |
| 56 | console.log('\n🔐 Google OAuth Setup for Addie\n'); |
| 57 | |
| 58 | // Build the authorization URL |
| 59 | const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth'); |
| 60 | authUrl.searchParams.set('client_id', CLIENT_ID!); |
| 61 | authUrl.searchParams.set('redirect_uri', REDIRECT_URI); |
| 62 | authUrl.searchParams.set('response_type', 'code'); |
| 63 | authUrl.searchParams.set('scope', SCOPES.join(' ')); |
| 64 | authUrl.searchParams.set('access_type', 'offline'); |
| 65 | authUrl.searchParams.set('prompt', 'consent'); // Force refresh token generation |
| 66 | |
| 67 | // Start local server to receive the callback |
| 68 | const server = http.createServer(async (req, res) => { |
| 69 | const url = new URL(req.url!, `http://localhost:${REDIRECT_PORT}`); |
| 70 | |
| 71 | if (url.pathname === '/callback') { |
| 72 | const code = url.searchParams.get('code'); |
| 73 | const error = url.searchParams.get('error'); |
| 74 | |
| 75 | if (error) { |
| 76 | res.writeHead(400, { 'Content-Type': 'text/html' }); |
| 77 | res.end(`<h1>Error: ${error}</h1><p>Please try again.</p>`); |
| 78 | console.error(`\n❌ OAuth error: ${error}`); |
| 79 | process.exit(1); |
| 80 | } |
| 81 | |
| 82 | if (code) { |
| 83 | res.writeHead(200, { 'Content-Type': 'text/html' }); |
| 84 | res.end('<h1>✅ Success!</h1><p>You can close this window and return to the terminal.</p>'); |
| 85 | |
| 86 | try { |
| 87 | const tokens = await exchangeCodeForTokens(code); |
| 88 | |
| 89 | console.log('\n✅ OAuth successful!\n'); |
| 90 | console.log('Add these to your environment variables:\n'); |
| 91 | console.log('─'.repeat(60)); |
| 92 | console.log(`GOOGLE_CLIENT_ID=${CLIENT_ID}`); |
| 93 | console.log(`GOOGLE_CLIENT_SECRET=${CLIENT_SECRET}`); |
| 94 | console.log(`GOOGLE_REFRESH_TOKEN=${tokens.refresh_token}`); |
| 95 | console.log('─'.repeat(60)); |
| 96 | console.log('\nFor Fly.io, run:'); |
| 97 | console.log(` fly secrets set GOOGLE_CLIENT_ID="${CLIENT_ID}" GOOGLE_CLIENT_SECRET="${CLIENT_SECRET}" GOOGLE_REFRESH_TOKEN="${tokens.refresh_token}"`); |
| 98 | console.log('\n'); |
| 99 | |
| 100 | server.close(); |
| 101 | process.exit(0); |
| 102 | } catch (err) { |
| 103 | console.error('\n❌ Failed to exchange code for tokens:', err); |
| 104 | process.exit(1); |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | }); |
| 109 | |
| 110 | server.listen(REDIRECT_PORT, () => { |
| 111 | console.log(`Starting local server on port ${REDIRECT_PORT}...`); |
| 112 | console.log('\nOpening browser for Google sign-in...'); |
no test coverage detected