UNPKG

longcelot-sheet-db

Version:

Google Sheets-backed staging database adapter for Node.js with schema-first design

97 lines 4.47 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.readTokens = readTokens; exports.saveTokens = saveTokens; exports.resolveTokens = resolveTokens; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const chalk_1 = __importDefault(require("chalk")); const inquirer_1 = __importDefault(require("inquirer")); const cliFiles_1 = require("../../utils/cliFiles"); const oauthCallbackServer_1 = require("./oauthCallbackServer"); const browser_1 = require("./browser"); function readTokens() { const tokenPath = (0, cliFiles_1.resolveTokensPath)(); if (!fs_1.default.existsSync(tokenPath)) return null; try { return JSON.parse(fs_1.default.readFileSync(tokenPath, 'utf-8')); } catch { return null; } } function saveTokens(tokens) { const tokenPath = path_1.default.join(process.cwd(), cliFiles_1.TOKENS_FILENAME); // mode: 0o600 — owner read/write only. This file holds a Google OAuth refresh_token, which is a // long-lived bearer credential for the admin's Sheets/Drive access; default file permissions // (typically 0o644, world/group-readable) would leave it exposed to any other local user or // process on a shared machine or CI runner. fs_1.default.writeFileSync(tokenPath, JSON.stringify(tokens, null, 2), { encoding: 'utf-8', mode: 0o600 }); try { fs_1.default.chmodSync(tokenPath, 0o600); } catch { // Best-effort — e.g. unsupported on some Windows filesystems. writeFileSync's own `mode` // above already covers the common case (file didn't previously exist with looser permissions). } } /** * Prompts for the authorization code by hand — the original flow, kept as-is. Used whenever * automatic capture (see `tryCaptureViaLoopback`) isn't possible or doesn't complete, so a * non-loopback redirect URI, a busy port, a closed tab, or a timeout all degrade to this * rather than failing outright. */ async function promptForCode() { const { code } = await inquirer_1.default.prompt([ { type: 'input', name: 'code', message: 'Paste the authorization code from the redirect URL:', validate: (v) => (v.trim().length > 0 ? true : 'Code cannot be empty'), }, ]); return code.trim(); } /** * Refreshes stored tokens if present, otherwise walks the user through the interactive * browser OAuth flow. Shared by every CLI command that needs to talk to the Sheets API * (auth, sync, drop-table, drop-column, rename-column). * * @param options.force Skip the stored refresh token and force a fresh consent screen even if * a valid one is on disk — used by `lsdb auth --force`. */ async function resolveTokens(oauth, options = {}) { const stored = readTokens(); if (!options.force && stored?.refresh_token) { try { console.log(chalk_1.default.cyan('🔄 Refreshing OAuth tokens...\n')); const refreshed = await oauth.refreshTokens(stored.refresh_token); const merged = { ...stored, ...refreshed }; saveTokens(merged); return merged; } catch { console.log(chalk_1.default.yellow('⚠️ Token refresh failed. Re-authorizing...\n')); } } const authUrl = oauth.getAuthUrl(); console.log(chalk_1.default.cyan('🔐 Authorization required.\n')); console.log(chalk_1.default.white('Opening your browser to authorize lsdb with Google...')); console.log(chalk_1.default.gray('If it does not open automatically, visit this URL:\n')); console.log(chalk_1.default.bold.underline(authUrl)); console.log(); (0, browser_1.openBrowser)(authUrl); // Try to catch Google's redirect ourselves first; fall back to the manual-paste prompt for // anything automatic capture can't handle (non-loopback redirect URI, port in use, timeout...). const captured = await (0, oauthCallbackServer_1.tryCaptureViaLoopback)(oauth.getRedirectUri()); const code = captured ?? (await promptForCode()); const tokens = await oauth.getTokens(code); saveTokens(tokens); console.log(chalk_1.default.green(`✅ Tokens saved to ${cliFiles_1.TOKENS_FILENAME}\n`)); return tokens; } //# sourceMappingURL=oauthFlow.js.map