UNPKG

avocavo

Version:

Avocavo CLI - Nutrition analysis made simple. Get accurate USDA nutrition data with secure authentication.

263 lines (215 loc) • 7.79 kB
const axios = require('axios'); const chalk = require('chalk'); const ora = require('ora'); const Table = require('cli-table3'); const inquirer = require('inquirer'); class KeyManager { constructor(auth, baseUrl = 'https://app.avocavo.app') { this.auth = auth; this.baseUrl = baseUrl; } async getAuthHeaders() { const jwt = await this.auth.getJwtToken(); if (!jwt) { throw new Error('Not logged in. Run: avocavo login'); } return { 'Authorization': `Bearer ${jwt}`, 'Content-Type': 'application/json' }; } async list() { const spinner = ora('Fetching API keys...').start(); try { const headers = await this.getAuthHeaders(); const response = await axios.get(`${this.baseUrl}/api/keys`, { headers }); spinner.stop(); if (!response.data.keys || response.data.keys.length === 0) { console.log(chalk.yellow('šŸ“‹ No API keys found.')); console.log(chalk.cyan('šŸ’” Create one with: avocavo keys create')); return []; } const table = new Table({ head: ['Name', 'Key', 'Tier', 'Usage', 'Created'], colWidths: [20, 20, 12, 15, 20] }); const userInfo = this.auth.getUserInfo(); const currentKey = await this.auth.getApiKey(); response.data.keys.forEach(key => { const isActive = currentKey && currentKey.includes(key.api_key.slice(0, -3)); const keyDisplay = isActive ? chalk.green(`${key.api_key} āœ“`) : key.api_key; table.push([ key.key_name, keyDisplay, key.tier, `${key.monthly_usage}/${key.monthly_limit}`, new Date(key.created_at).toLocaleDateString() ]); }); console.log(chalk.cyan('\nšŸ“‹ Your API Keys:')); console.log(table.toString()); return response.data.keys; } catch (error) { spinner.fail('Failed to fetch API keys'); if (error.response?.status === 401) { console.error(chalk.red('āŒ Session expired. Please login again.')); } else { console.error(chalk.red(`āŒ Error: ${error.message}`)); } throw error; } } async create(options = {}) { let spinner; try { const headers = await this.getAuthHeaders(); // If no name provided via command line, ask interactively let keyName = options.name; let description = options.description; if (!keyName || keyName === 'CLI Key') { const answers = await inquirer.prompt([ { type: 'input', name: 'name', message: 'Enter a name for your API key:', default: 'CLI Key', validate: input => input.trim() ? true : 'Key name cannot be empty' }, { type: 'input', name: 'description', message: 'Enter a description (optional):', default: description || 'Created via CLI' } ]); keyName = answers.name; description = answers.description; } spinner = ora('Creating API key...').start(); const data = { key_name: keyName, description: description, environment: options.environment || 'development' }; const response = await axios.post(`${this.baseUrl}/api/keys`, data, { headers }); spinner.succeed('API key created successfully!'); const key = response.data.key; console.log(chalk.green('\nāœ… New API Key Created:')); console.log(chalk.cyan(` Name: ${key.key_name}`)); console.log(chalk.yellow(` Key: ${key.api_key}`)); console.log(chalk.gray('\nāš ļø Save this key securely - you won\'t be able to see it again!')); // Ask if user wants to use this key now const { useNow } = await inquirer.prompt([{ type: 'confirm', name: 'useNow', message: 'Use this API key now?', default: true }]); if (useNow) { await this.switch(key.api_key); } return key; } catch (error) { if (spinner) { spinner.fail('Failed to create API key'); } console.error(chalk.red(`āŒ Error: ${error.response?.data?.error || error.message}`)); throw error; } } async switch(apiKey) { try { // If no key provided, show interactive selection if (!apiKey) { const keys = await this.list(); if (keys.length === 0) return; const choices = keys.map(k => ({ name: `${k.key_name} (${k.tier}) - ${k.api_key}`, value: k.id // Pass the ID instead of the whole object })); const { selectedKeyId } = await inquirer.prompt([{ type: 'list', name: 'selectedKeyId', message: 'Select an API key to use:', choices }]); // Now reveal the full API key const headers = await this.getAuthHeaders(); const response = await axios.post( `${this.baseUrl}/api/keys/${selectedKeyId}/reveal`, {}, { headers } ); apiKey = response.data.api_key; } // Reveal the full key if it's masked if (apiKey.includes('...')) { const spinner = ora('Retrieving full API key...').start(); try { const headers = await this.getAuthHeaders(); const keyId = apiKey.match(/\d+/)?.[0]; // Extract ID if present if (!keyId) { spinner.fail('Invalid key format'); return; } const response = await axios.post( `${this.baseUrl}/api/keys/${keyId}/reveal`, {}, { headers } ); apiKey = response.data.api_key; spinner.succeed('API key retrieved'); } catch (error) { spinner.fail('Failed to retrieve API key'); throw error; } } // Store the API key const userInfo = this.auth.getUserInfo(); await this.auth.storeApiKeySecurely(userInfo.email, apiKey); console.log(chalk.green('āœ… Switched to API key successfully!')); console.log(chalk.cyan(`šŸ”‘ Active key: ${apiKey.substring(0, 12)}...`)); } catch (error) { console.error(chalk.red(`āŒ Error: ${error.message}`)); throw error; } } async delete(keyId) { try { if (!keyId) { const keys = await this.list(); if (keys.length === 0) return; const choices = keys.map(k => ({ name: `${k.key_name} (${k.tier}) - ${k.api_key}`, value: k.id })); const { selectedId } = await inquirer.prompt([{ type: 'list', name: 'selectedId', message: 'Select an API key to delete:', choices }]); keyId = selectedId; } // Confirm deletion const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: 'Are you sure you want to delete this API key?', default: false }]); if (!confirm) { console.log(chalk.yellow('āŒ Deletion cancelled')); return; } const spinner = ora('Deleting API key...').start(); const headers = await this.getAuthHeaders(); await axios.delete(`${this.baseUrl}/api/keys/${keyId}`, { headers }); spinner.succeed('API key deleted successfully!'); } catch (error) { console.error(chalk.red(`āŒ Error: ${error.message}`)); throw error; } } } module.exports = KeyManager;