Skip to content

Commit

Permalink
feat: cli and rename env variables
Browse files Browse the repository at this point in the history
  • Loading branch information
atinux committed Feb 16, 2024
1 parent 1cddd91 commit 5b0a167
Show file tree
Hide file tree
Showing 13 changed files with 319 additions and 49 deletions.
61 changes: 61 additions & 0 deletions cli/commands/login.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { consola } from 'consola'
import { defineCommand, runCommand } from 'citty'
import { loadConfig, isHeadless, writeConfig, $api } from '../utils.mjs'
import { createApp, eventHandler, toNodeListener, getQuery } from 'h3'
import { getRandomPort } from 'get-port-please'
import { listen } from 'listhen'
import { withQuery, joinURL } from 'ufo'
import { NUXT_HUB_URL } from '../utils.mjs'
import whoami from './whoami.mjs'

export default defineCommand({
meta: {
name: 'login',
description: 'Authenticate with NuxtHub',
},
async setup() {
if (isHeadless()) {
throw new Error('nuxthub login is not supported in Docker or SSH yet.')
}
const config = loadConfig()
if (config.token) {
return runCommand(whoami, {})
}
// Create server for OAuth flow
let listener
const stopListener = () => setTimeout(() => listener.close(), 1000)
const app = createApp()
let handled = false
app.use('/', eventHandler(async (event) => {
if (handled) return
handled = true
const token = getQuery(event).token

if (token) {
const user = await $api('/user').catch(() => null)
if (user?.name) {
writeConfig({ token })
consola.success('Authenticated successfully!')

stopListener()

// TODO: redirect to success CLI page: https://hub.nuxt.com/cli/login-success?name=${user.name}
return 'Authenticated successfully! You can close this window now.'
}
}
consola.error('Authentication error, please try again.')
stopListener()
return 'Authentication error, missing token.'
}))
const randomPort = await getRandomPort()
listener = await listen(toNodeListener(app), {
showURL: false,
port: randomPort
})
consola.box(
'Please visit the following URL in your web browser:\n\n'+
withQuery(joinURL(NUXT_HUB_URL, '/api/cli/authorize'), { redirect: listener.url })
)
consola.info('Waiting for authentication to be completed...')
},
})
27 changes: 27 additions & 0 deletions cli/commands/logout.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { consola } from 'consola'
import { defineCommand } from 'citty'
import { loadConfig, writeConfig } from '../utils.mjs'
import { $api } from '../utils.mjs'

export default defineCommand({
meta: {
name: 'logout',
description: 'Logout the current authenticated user.',
},
async setup() {
const config = loadConfig()
if (!config.token) {
consola.info('Not currently logged in.')
return
}

writeConfig({
...config,
token: ''
})
await $api('/user/token', {
method: 'DELETE'
}).catch(() => {})
consola.info('You have been logged out.')
},
})
25 changes: 25 additions & 0 deletions cli/commands/whoami.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { consola } from 'consola'
import { defineCommand } from 'citty'
import { loadConfig } from '../utils.mjs'
import { $api } from '../utils.mjs'

export default defineCommand({
meta: {
name: 'whoami',
description: 'Shows the username of the currently logged in user.',
},
async setup() {
const config = loadConfig()
if (!config.token) {
consola.log('Not currently logged in.')
return
}

const user = await $api('/user')
if (!user?.name) {
consola.log('Not currently logged in.')
return
}
consola.info(`Logged in as \`${user.name}\``)
},
})
26 changes: 26 additions & 0 deletions cli/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env node
import { defineCommand, runMain } from 'citty'
import consola from 'consola'
import { colors } from 'consola/utils'
import login from './commands/login.mjs'
import logout from './commands/logout.mjs'
import whoami from './commands/whoami.mjs'

const main = defineCommand({
meta: {
name: 'nuxthub',
description: 'NuxtHub CLI'
},
setup({ args, cmd }) {
if (args._.length) {
consola.log(colors.gray(`${cmd.meta.description}`))
}
},
subCommands: {
login,
logout,
whoami
},
})

runMain(main)
37 changes: 37 additions & 0 deletions cli/utils.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import isDocker from 'is-docker'
import { joinURL } from 'ufo'
import { join } from 'pathe'
import XDGAppPaths from 'xdg-app-paths'
import { loadJsonFileSync } from 'load-json-file'
import { writeJsonFileSync } from 'write-json-file'
import { ofetch } from 'ofetch'

const CONFIG_DIR = XDGAppPaths('com.nuxt.hub.cli').dataDirs()[0]
const CONFIG_PATH = join(CONFIG_DIR, 'auth.json')
export const NUXT_HUB_URL = process.env.NUXT_HUB_URL || 'http://nuxthub-admin.pages.dev'

export function loadConfig() {
try {
return loadJsonFileSync(CONFIG_PATH)
} catch (err) {
return {}
}
}
export function writeConfig (config) {
try {
return writeJsonFileSync(CONFIG_PATH, config, { indent: 2 })
} catch (err) {
console.error(`Not able to create ${CONFIG_PATH}.`, err)
}
}

export function isHeadless() {
return isDocker() || Boolean(process.env.SSH_CLIENT || process.env.SSH_TTY)
}

export const $api = ofetch.create({
baseURL: joinURL(NUXT_HUB_URL, '/api'),
headers: {
Authorization: `token ${loadConfig().token || ''}`
}
})
4 changes: 2 additions & 2 deletions modules/hub/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ export default defineNuxtModule({
return
}

if (process.env.NUXT_HUB_URL) {
if (process.env.NUXT_HUB_PROJECT_URL) {
// TODO: check on hub.nuxt.com if the project is connected
logger.info(`Using remote hub from \`${process.env.NUXT_HUB_URL}\``)
logger.info(`Using remote hub from \`${process.env.NUXT_HUB_PROJECT_URL}\``)
return
} else {
logger.info('Using local hub from bindings')
Expand Down
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
"plugins",
"server",
"types",
"cli",
"nuxt.config.ts"
],
"bin": {
"nuxthub": "cli/index.mjs"
},
"license": "PROPRIETARY",
"scripts": {
"dev:prepare": "nuxt prepare _demo",
Expand All @@ -28,11 +32,21 @@
"@cloudflare/workers-types": "^4.20240208.0",
"@sindresorhus/slugify": "^2.2.1",
"@uploadthing/mime-types": "^0.2.2",
"citty": "^0.1.6",
"consola": "^3.2.3",
"get-port-please": "^3.1.2",
"h3": "npm:h3-nightly@1.10.2-1706531092.e38d444",
"is-docker": "^3.0.0",
"listhen": "^1.6.0",
"load-json-file": "^7.0.1",
"mime": "^4.0.1",
"nuxt-auth-utils": "^0.0.17",
"pathe": "^1.1.2",
"pkg-types": "^1.0.3",
"uncrypto": "^0.1.3",
"wrangler": "^3.28.2",
"write-json-file": "^5.0.0",
"xdg-app-paths": "^8.3.0",
"zod": "^3.22.4"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit 5b0a167

Please sign in to comment.