UNPKG

@capawesome/cli

Version:

The Capawesome Cloud Command Line Interface (CLI) to manage Live Updates and more.

129 lines (128 loc) 5.52 kB
import configService from '../services/config.js'; import sessionCodesService from '../services/session-code.js'; import sessionsService from '../services/sessions.js'; import usersService from '../services/users.js'; import { isInteractive } from '../utils/environment.js'; import { prompt } from '../utils/prompt.js'; import userConfig from '../utils/user-config.js'; import { defineCommand, defineOptions } from '@robingenz/zli'; import { AxiosError } from 'axios'; import consola from 'consola'; import open from 'open'; import { z } from 'zod'; export default defineCommand({ description: 'Sign in to the Capawesome Cloud Console.', options: defineOptions(z.object({ token: z.string().optional().describe('Token to use for authentication.'), })), action: async (options, args) => { const consoleBaseUrl = await configService.getValueForKey('CONSOLE_BASE_URL'); let { token: sessionIdOrToken } = options; if (sessionIdOrToken === undefined) { if (!isInteractive()) { consola.error('You must provide a token when running in non-interactive environment.'); process.exit(1); } // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged const authenticationMethod = await prompt('How would you like to authenticate Capawesome CLI?', { type: 'select', options: [ { label: 'Login with a web browser', value: 'browser' }, { label: 'Paste an authentication token', value: 'token' }, ], }); if (authenticationMethod === 'browser') { // Create a session code const { id: deviceCode, code: userCode } = await sessionCodesService.create(); consola.box(`Copy your one-time code: ${userCode.slice(0, 4)}-${userCode.slice(4)}`); // Prompt the user to open the authorization URL in their browser const shouldProceed = await prompt(`Select Yes to continue in your browser or No to cancel the authentication.`, { type: 'confirm', initial: true, }); if (!shouldProceed) { consola.error('Authentication cancelled.'); process.exit(1); } // Open the authorization URL in the user's default browser consola.start('Opening browser...'); const authorizationUrl = `${consoleBaseUrl}/login/device`; try { open(authorizationUrl); } catch (error) { consola.warn(`Could not open browser automatically. Please open the following URL manually: ${authorizationUrl}`); } // Wait for the user to authenticate consola.start('Waiting for authentication...'); const sessionId = await createSession(deviceCode); if (!sessionId) { consola.error('Authentication timed out. Please try again.'); process.exit(1); } sessionIdOrToken = sessionId; } else { consola.info(`You can create a token at ${consoleBaseUrl}/settings/tokens.`); // Prompt the user to enter their token sessionIdOrToken = await prompt('Please provide your authentication token:', { type: 'text', }); if (!sessionIdOrToken) { consola.error('Token must be provided.'); process.exit(1); } } } else if (sessionIdOrToken.length === 0) { // No token provided consola.error(`Please provide a valid token. You can create a token at ${consoleBaseUrl}/settings/tokens.`); process.exit(1); } // Sign in with the provided token consola.start('Signing in...'); userConfig.write({ token: sessionIdOrToken, }); try { await usersService.me(); consola.success(`Successfully signed in.`); } catch (error) { userConfig.write({}); if (error instanceof AxiosError && error.response?.status === 401) { consola.error(`Invalid token. Please provide a valid token. You can create a token at ${consoleBaseUrl}/settings/tokens.`); process.exit(1); } else { throw error; } } }, }); const createSession = async (deviceCode) => { const maxAttempts = 20; const interval = 3 * 1000; // 3 seconds let attempts = 0; let sessionId = null; while (attempts < maxAttempts && sessionId === null) { try { const response = await sessionsService.create({ code: deviceCode, provider: 'code', }); sessionId = response.id; } catch (error) { if (error instanceof AxiosError && error.response?.status === 400) { // Session not ready yet, wait and try again attempts++; await new Promise((resolve) => setTimeout(resolve, interval)); } else { throw error; } } } return sessionId; };