UNPKG

keyvault-cli

Version:

Secure API key management CLI tool

256 lines (255 loc) 9.82 kB
import fs from 'fs'; import path from 'path'; import os from 'os'; import chalk from 'chalk'; import inquirer from 'inquirer'; export class CLIAuth { static async login() { console.log(chalk.blue('🔐 KeyVault Login')); const apiUrl = 'https://1pass.vercel.app'; const { email, password } = await inquirer.prompt([ { type: 'input', name: 'email', message: 'Email:', validate: (input) => input.includes('@') || 'Please enter a valid email' }, { type: 'password', name: 'password', message: 'Password:', mask: '*' } ]); try { // API'ye login isteği gönder const fetch = (await import('node-fetch')).default; const response = await fetch(`${apiUrl}/api/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); if (!response.ok) { throw new Error('Invalid credentials'); } const data = await response.json(); // Token'ı güvenli şekilde sakla await this.saveConfig({ apiUrl, token: data.token, email }); console.log(chalk.green('✅ Successfully logged in!')); console.log(chalk.gray(`Logged in as: ${email}`)); } catch (error) { console.log(chalk.red('❌ Login failed:'), error instanceof Error ? error.message : 'Unknown error'); process.exit(1); } } static async logout() { try { await fs.promises.unlink(this.configPath); console.log(chalk.green('✅ Successfully logged out!')); } catch (error) { console.log(chalk.yellow('⚠️ No active session found')); } } static async getConfig() { try { const configData = await fs.promises.readFile(this.configPath, 'utf8'); return JSON.parse(configData); } catch (error) { return null; } } static async isLoggedIn() { const config = await this.getConfig(); return !!(config?.token); } static async requireAuth() { const config = await this.getConfig(); if (!config?.token) { console.log(chalk.red('❌ Not logged in. Please run "keyvault login" first.')); process.exit(1); } return config; } static async saveConfig(config) { const configDir = path.dirname(this.configPath); // Config klasörünü oluştur await fs.promises.mkdir(configDir, { recursive: true }); // Config'i kaydet await fs.promises.writeFile(this.configPath, JSON.stringify(config, null, 2)); // Dosya izinlerini sıkılaştır (sadece owner okuyabilir) await fs.promises.chmod(this.configPath, 0o600); } static async savePersonalKeys(keys) { const config = await this.getConfig(); if (!config) { throw new Error('No config found. Please login first.'); } config.personalKeys = keys; await this.saveConfig(config); } static async saveTeamKeys(teamName, keys) { const config = await this.getConfig(); if (!config) { throw new Error('No config found. Please login first.'); } if (!config.teamKeys) { config.teamKeys = {}; } config.teamKeys[teamName] = keys; await this.saveConfig(config); } static async getPersonalKeys() { const config = await this.getConfig(); return config?.personalKeys || null; } static async getTeamKeys(teamName) { const config = await this.getConfig(); if (!config?.teamKeys) return null; if (teamName) { return config.teamKeys[teamName] || null; } return config.teamKeys; } static async whoami() { const config = await this.getConfig(); if (!config?.token) { console.log(chalk.yellow('Not logged in')); return; } console.log(chalk.blue('Current session:')); console.log(` Email: ${config.email}`); console.log(` API URL: ${config.apiUrl}`); if (config.activeTeamId) { try { // Fetch team name from API const fetch = (await import('node-fetch')).default; const response = await fetch(`${config.apiUrl}/api/teams/${config.activeTeamId}`, { headers: { 'Authorization': `Bearer ${config.token}` } }); if (response.ok) { const data = await response.json(); const team = data.team || data; console.log(` Active Team: ${team.name} - ${config.activeTeamId}`); } else { console.log(` Active Team: ${config.activeTeamId} (name unavailable)`); } } catch (error) { console.log(` Active Team: ${config.activeTeamId} (name unavailable)`); } } else { console.log(chalk.gray(' No active team selected')); } } static async forgotPassword() { console.log(chalk.blue('🔐 KeyVault Password Reset')); const apiUrl = 'https://1pass.vercel.app'; const { email } = await inquirer.prompt([ { type: 'input', name: 'email', message: 'Your email address:', validate: (input) => input.includes('@') || 'Please enter a valid email' } ]); try { const fetch = (await import('node-fetch')).default; const response = await fetch(`${apiUrl}/api/auth/forgot-password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }) }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Request failed'); } const data = await response.json(); console.log(chalk.green('✅ Password reset email sent!')); console.log(chalk.gray('Check your email for reset instructions.')); // In development mode, show additional info if (data.resetToken) { console.log(chalk.yellow('\n🔧 Development Mode:')); console.log(chalk.gray('Reset token:', data.resetToken)); if (data.info) { console.log(chalk.gray(data.info)); } // Ask if user wants to reset password immediately const { useToken } = await inquirer.prompt([ { type: 'confirm', name: 'useToken', message: 'Do you want to reset your password now? (dev mode)', default: false } ]); if (useToken) { await this.resetPassword(apiUrl, data.resetToken); } } } catch (error) { console.log(chalk.red('❌ Request failed:'), error instanceof Error ? error.message : 'Unknown error'); } } static async resetPassword(apiUrl, token) { console.log(chalk.blue('🔑 KeyVault Password Reset')); let resetToken = token; if (!resetToken) { const { inputToken } = await inquirer.prompt([ { type: 'input', name: 'inputToken', message: 'Enter your reset token:', validate: (input) => input.length > 0 || 'Reset token is required' } ]); resetToken = inputToken; } const { newPassword, confirmPassword } = await inquirer.prompt([ { type: 'password', name: 'newPassword', message: 'New password:', mask: '*', validate: (input) => input.length >= 6 || 'Password must be at least 6 characters' }, { type: 'password', name: 'confirmPassword', message: 'Confirm new password:', mask: '*', validate: (input, answers) => input === answers.newPassword || 'Passwords do not match' } ]); try { const fetch = (await import('node-fetch')).default; const response = await fetch(`${apiUrl}/api/auth/reset-password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: resetToken, newPassword }) }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || 'Request failed'); } console.log(chalk.green('✅ Password reset successfully!')); console.log(chalk.gray('You can now login with your new password.')); } catch (error) { console.log(chalk.red('❌ Password reset failed:'), error instanceof Error ? error.message : 'Unknown error'); } } } CLIAuth.configPath = path.join(os.homedir(), '.keyvault', 'config.json');