-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
319 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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...') | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.') | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}\``) | ||
}, | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 || ''}` | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.