settings site changes
This commit is contained in:
@@ -1,20 +1,28 @@
|
||||
/**
|
||||
* One-time script to generate a Gmail OAuth2 refresh token.
|
||||
* Run once, copy the refresh token into .env, then delete this file.
|
||||
*
|
||||
* Usage:
|
||||
* node get-refresh-token.js
|
||||
*
|
||||
* Prerequisites:
|
||||
* - GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET set in .env (or exported in shell)
|
||||
* - GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET set in .env
|
||||
* - In Google Cloud Console → OAuth 2.0 Client → Authorized redirect URIs:
|
||||
* add http://localhost:3000/oauth2callback
|
||||
*
|
||||
* Flow:
|
||||
* 1. Script prints an auth URL. Open it in any browser (same host or remote).
|
||||
* 2a. Same-host browser: redirect to localhost:3000 is captured automatically.
|
||||
* 2b. Remote browser (e.g. Windows, script running on Linux over SSH):
|
||||
* the browser fails to load localhost:3000/oauth2callback?code=... — copy
|
||||
* the full URL (or just the code= value) from the address bar and paste
|
||||
* it into the terminal prompt.
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
const { google } = require('googleapis');
|
||||
const http = require('http');
|
||||
const url = require('url');
|
||||
const readline = require('readline');
|
||||
|
||||
const CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
|
||||
const CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET;
|
||||
@@ -41,29 +49,37 @@ console.log('\n=== Gmail OAuth2 Token Generator ===\n');
|
||||
console.log('1. Open this URL in your browser:\n');
|
||||
console.log(authUrl);
|
||||
console.log('\n2. Authorize the app with your support Gmail account.');
|
||||
console.log('3. You will be redirected to localhost — this script will capture the token.\n');
|
||||
console.log('\n3a. If the browser runs on this host, the redirect is captured automatically.');
|
||||
console.log('3b. If the browser is on a different machine, it will fail to load');
|
||||
console.log(' http://localhost:3000/oauth2callback?code=... — copy the full URL');
|
||||
console.log(' (or just the code= value) from the address bar and paste it below.\n');
|
||||
|
||||
// Temporary local server to capture the callback
|
||||
const server = http.createServer(async (req, res) => {
|
||||
const parsed = url.parse(req.url, true);
|
||||
if (parsed.pathname !== '/oauth2callback') {
|
||||
res.end('Not found');
|
||||
return;
|
||||
}
|
||||
let done = false;
|
||||
|
||||
const code = parsed.query.code;
|
||||
if (!code) {
|
||||
res.end('No code received.');
|
||||
server.close();
|
||||
return;
|
||||
function extractCode(input) {
|
||||
const trimmed = (input || '').trim();
|
||||
if (!trimmed) return null;
|
||||
try {
|
||||
// If the user pastes a full URL, pull the `code` query param.
|
||||
// URL() treats localhost URLs fine; searchParams auto-decodes.
|
||||
const parsed = new URL(trimmed);
|
||||
return parsed.searchParams.get('code');
|
||||
} catch {
|
||||
// Not a URL — assume the user pasted the raw code.
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
async function finish(code, source, httpRespond) {
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
try {
|
||||
const { tokens } = await oauth2Client.getToken(code);
|
||||
res.end('<h2>Success! Check your terminal for the refresh token.</h2><p>You can close this tab.</p>');
|
||||
server.close();
|
||||
|
||||
console.log('\n=== SUCCESS ===\n');
|
||||
if (httpRespond) {
|
||||
httpRespond('<h2>Success! Check your terminal for the refresh token.</h2><p>You can close this tab.</p>');
|
||||
}
|
||||
console.log(`\n=== SUCCESS (via ${source}) ===\n`);
|
||||
console.log('Add this to your .env:\n');
|
||||
console.log(`REFRESH_TOKEN=${tokens.refresh_token}`);
|
||||
if (!tokens.refresh_token) {
|
||||
@@ -73,12 +89,51 @@ const server = http.createServer(async (req, res) => {
|
||||
console.log('\nAll tokens (for reference):');
|
||||
console.log(JSON.stringify(tokens, null, 2));
|
||||
} catch (err) {
|
||||
res.end('Error exchanging code: ' + err.message);
|
||||
server.close();
|
||||
console.error('Error:', err);
|
||||
if (httpRespond) httpRespond('Error exchanging code: ' + err.message);
|
||||
console.error('\nError exchanging code:', err.response?.data || err.message);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
try { server.close(); } catch {}
|
||||
try { rl.close(); } catch {}
|
||||
}
|
||||
}
|
||||
|
||||
const server = http.createServer(async (req, res) => {
|
||||
const parsed = url.parse(req.url, true);
|
||||
if (parsed.pathname !== '/oauth2callback') {
|
||||
res.end('Not found');
|
||||
return;
|
||||
}
|
||||
const code = parsed.query.code;
|
||||
if (!code) {
|
||||
res.end('No code received.');
|
||||
return;
|
||||
}
|
||||
await finish(code, 'browser redirect', (body) => res.end(body));
|
||||
});
|
||||
|
||||
server.on('error', (err) => {
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
console.log('Port 3000 is in use — auto-capture disabled. Use the paste prompt below.\n');
|
||||
} else {
|
||||
console.error('HTTP server error:', err.message);
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(3000, () => {
|
||||
console.log('Waiting for OAuth callback on http://localhost:3000 ...\n');
|
||||
console.log('Waiting for OAuth callback on http://localhost:3000 (or paste below) ...\n');
|
||||
});
|
||||
|
||||
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
||||
rl.question('Paste redirect URL or code here: ', async (answer) => {
|
||||
if (done) return;
|
||||
const code = extractCode(answer);
|
||||
if (!code) {
|
||||
console.error('Could not parse a code from that input.');
|
||||
process.exitCode = 1;
|
||||
try { server.close(); } catch {}
|
||||
rl.close();
|
||||
return;
|
||||
}
|
||||
await finish(code, 'pasted input');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user