nuxthub
Version:
Interface with the NuxtHub platform from the command line.
91 lines (87 loc) • 3.35 kB
JavaScript
import { hostname } from 'os'
import { consola } from 'consola'
import { colors } from 'consola/utils'
import { defineCommand } from 'citty'
import { isHeadless, fetchUser, updateUserConfig, $api, NUXT_HUB_URL } from '../utils/index.mjs'
import { createApp, eventHandler, toNodeListener, getQuery, sendRedirect } from 'h3'
import { getRandomPort } from 'get-port-please'
import { listen } from 'listhen'
import { withQuery, joinURL } from 'ufo'
import open from 'open'
export default defineCommand({
meta: {
name: 'login',
description: 'Authenticate with NuxtHub.',
},
async run() {
if (isHeadless()) {
throw new Error('nuxthub login is not supported in headless mode yet.')
}
const user = await fetchUser()
if (user) {
return consola.info(`Already logged in as ${colors.blueBright(user.name)}`)
}
// Create server for OAuth flow
let listener
const app = createApp()
let handled = false
// Get machine name
const host = hostname().replace(/-/g, ' ').replace('.local', '').replace('.home', '').toLowerCase()
const tokenName = `NuxtHub CLI on ${host}`
// eslint-disable-next-line no-async-promise-executor
await new Promise(async (resolve) => {
app.use('/', eventHandler(async (event) => {
if (handled) return
handled = true
const code = getQuery(event).code
if (code) {
const { token } = await $api('/cli/verify', {
method: 'POST',
body: {
code,
name: tokenName
}
}).catch((err) => {
console.error('Failed to verify session', err.message)
return { token: null }
})
const user = await $api('/user', {
headers: {
Authorization: `Bearer ${token}`
}
}).catch((err) => {
console.error('Failed to fetch user', err.message)
return null
})
if (user?.name) {
updateUserConfig({ hub: { userToken: token } })
consola.success('Authenticated successfully!')
resolve()
return sendRedirect(event, joinURL(NUXT_HUB_URL, '/cli/status?success'))
}
}
consola.error('Authentication error, please try again.')
resolve()
return sendRedirect(event, joinURL(NUXT_HUB_URL, '/cli/status?error'))
}))
const randomPort = await getRandomPort()
listener = await listen(toNodeListener(app), {
showURL: false,
port: randomPort
})
let redirect = listener.url
// if running inside GitHub Codespace
if (process.env.CODESPACE_NAME && process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN) {
redirect = `https://${process.env.CODESPACE_NAME}-${randomPort}.${process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}`
}
const authUrl = withQuery(joinURL(NUXT_HUB_URL, '/api/cli/authorize'), { redirect })
consola.info('Please visit the following URL in your web browser:')
consola.info(`\`${authUrl}\``)
consola.info('Waiting for authentication to be completed...')
open(authUrl)
})
// Close server after 1s to make sure we have time to handle the request
await new Promise((resolve) => setTimeout(resolve, 1000))
await listener.close()
},
})