Skip to content

Commit

Permalink
Start moving setup commands into provider specific folders
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobbe committed Aug 25, 2022
1 parent e9ae1cd commit 5cfe770
Show file tree
Hide file tree
Showing 11 changed files with 360 additions and 25 deletions.
23 changes: 0 additions & 23 deletions packages/cli/src/commands/setup/auth/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,29 +55,6 @@ async function shouldForce(force, provider, webAuthn) {
return false
}

/**
* Prompt the user (unless already specified on the command line) if they want
* to enable WebAuthn support if they're setting up an auth service provider
* that has support for it.
* Right now, only dbAuth supports WebAuthn, but in theory it could work with
* any provider, so we'll do a check here and potentially use the webAuthn
* version of its provider
*/
async function shouldIncludeWebAuthn(webauthn, provider) {
if (webauthn === null && WEBAUTHN_SUPPORTED_PROVIDERS.includes(provider)) {
const webAuthnResponse = await prompts({
type: 'confirm',
name: 'answer',
message: `Enable WebAuthn support (TouchID/FaceID)? See https://redwoodjs.com/docs/auth/dbAuth#webAuthn`,
initial: false,
})

return webAuthnResponse.answer
}

return webauthn
}

export const command = 'auth <provider>'
export const description = 'Generate an auth configuration'
export const builder = (yargs) => {
Expand Down
63 changes: 63 additions & 0 deletions packages/cli/src/commands/setup/auth/providers/dbAuth/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import prompts from 'prompts'
import terminalLink from 'terminal-link'
import yargs from 'yargs'
import { standardAuthHandler } from '../../setupHelpers'

/**
* Prompt the user (unless already specified on the command line) if they want
* to enable WebAuthn support
*/
async function shouldIncludeWebAuthn(webauthn: boolean) {
if (webauthn === null) {
const webAuthnResponse = await prompts({
type: 'confirm',
name: 'answer',
message: `Enable WebAuthn support (TouchID/FaceID)? See https://redwoodjs.com/docs/auth/dbAuth#webAuthn`,
initial: false,
})

return webAuthnResponse.answer
}

return webauthn
}

export const command = 'auth dbAuth'
export const description = 'Generate an auth configuration for dbAuth'
export const builder = (yargs: yargs.Argv) => {
yargs
.option('force', {
alias: 'f',
default: false,
description: 'Overwrite existing configuration',
type: 'boolean',
})
.option('webauthn', {
alias: 'w',
default: null,
description: 'Include WebAuthn support (TouchID/FaceID)',
type: 'boolean',
})
.epilogue(
`Also see the ${terminalLink(
'Redwood CLI Reference',
'https://redwoodjs.com/docs/cli-commands#setup-auth'
)}`
)
}

interface Args {
rwVersion: string
webauthn: boolean
force: boolean
}

export const handler = async ({ rwVersion, webauthn, force: forceArg }: Args) => {
const webAuthn = await shouldIncludeWebAuthn(webauthn)
standardAuthHandler({
rwVersion,
forceArg,
provider: 'dbAuth',
webAuthn,
})
}
22 changes: 22 additions & 0 deletions packages/cli/src/commands/setup/auth/providers/netlify/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import yargs from 'yargs'
import { standardAuthBuilder, standardAuthHandler } from '../../setupHelpers'

export const command = 'auth netlify'
export const description = 'Generate an auth configuration for Netlify'
export const builder = (yargs: yargs.Argv) => {
return standardAuthBuilder(yargs)
}

interface Args {
rwVersion: string
force: boolean
}

export const handler = async ({ rwVersion, force: forceArg }: Args) => {
standardAuthHandler({
rwVersion,
forceArg,
provider: 'netlify',
webAuthn: false,
})
}
22 changes: 22 additions & 0 deletions packages/cli/src/commands/setup/auth/providers/supabase/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import yargs from 'yargs'
import { standardAuthBuilder, standardAuthHandler } from '../../setupHelpers'

export const command = 'auth supabase'
export const description = 'Generate an auth configuration for Supabase'
export const builder = (yargs: yargs.Argv) => {
return standardAuthBuilder(yargs)
}

interface Args {
rwVersion: string
force: boolean
}

export const handler = async ({ rwVersion, force: forceArg }: Args) => {
standardAuthHandler({
rwVersion,
forceArg,
provider: 'supabase',
webAuthn: false,
})
}
129 changes: 129 additions & 0 deletions packages/cli/src/commands/setup/auth/setupHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import fs from 'fs'

import Listr from 'listr'
import prompts from 'prompts'
import terminalLink from 'terminal-link'
import yargs from 'yargs'

import { errorTelemetry } from '@redwoodjs/telemetry'

import { getPaths } from '../../../lib'
import c from '../../../lib/colors'

import { files } from './authFiles'
import {
addApiPackages,
addAuthConfigToGqlApi,
addAuthConfigToWeb,
addWebPackages,
generateAuthApi,
installPackages,
printNotes,
} from './authTasks'

/**
* Check if one of the api side auth files already exists and if so, ask the
* user to overwrite
*/
async function shouldForce(
force: boolean,
provider: string,
webAuthn: boolean
) {
if (force) {
return true
}

const existingFiles = Object.keys(files({ provider, webAuthn })).filter(
(filePath) => fs.existsSync(filePath)
)

if (existingFiles.length > 0) {
const shortPaths = existingFiles.map((filePath) =>
filePath.replace(getPaths().base, '')
)
const forceResponse = await prompts({
type: 'confirm',
name: 'answer',
message: `Overwrite existing ${shortPaths.join(', ')}?`,
initial: false,
})

return forceResponse.answer
}

return false
}

export const standardAuthBuilder = (yargs: yargs.Argv) => {
yargs
.option('force', {
alias: 'f',
default: false,
description: 'Overwrite existing configuration',
type: 'boolean',
})
.epilogue(
`Also see the ${terminalLink(
'Redwood CLI Reference',
'https://redwoodjs.com/docs/cli-commands#setup-auth'
)}`
)
}

interface Args {
rwVersion: string
forceArg: boolean
provider: string
webAuthn: boolean
}

export const standardAuthHandler = async ({
rwVersion,
forceArg,
provider,
webAuthn,
}: Args) => {
const force = await shouldForce(forceArg, provider, webAuthn)
const providerData = webAuthn
? await import(`./providers/${provider}/${provider}.webAuthn`)
: await import(`./providers/${provider}/${provider}`)

const tasks = new Listr(
[
generateAuthApi(provider, force, webAuthn),
addAuthConfigToWeb(provider),
addAuthConfigToGqlApi,
addWebPackages(provider, providerData.webPackages, rwVersion),
addApiPackages(provider, providerData.apiPackages),
installPackages,
providerData.task,
printNotes(providerData.notes),
].filter(Boolean),
// @ts-expect-error: This option is widely used, so I guess it works...
{ collapse: false }
)

try {
await tasks.run()
} catch (e) {
if (isErrorWithMessage(e)) {
errorTelemetry(process.argv, e.message)
console.error(c.error(e.message))
}

if (isErrorWithErrorCode(e)) {
process.exit(e.exitCode || 1)
} else {
process.exit(1)
}
}
}

function isErrorWithMessage(e: any): e is { message: string } {
return !!e.message
}

function isErrorWithErrorCode(e: any): e is { exitCode: number } {
return !isNaN(e.exitCode)
}
12 changes: 12 additions & 0 deletions packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.compilerOption.json",
"compilerOptions": {
"allowJs": true,
"baseUrl": ".",
"rootDir": "src",
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
"outDir": "dist"
},
"include": ["src"],
"references": [{ "path": "../telemetry" }]
}
File renamed without changes.
10 changes: 10 additions & 0 deletions packages/telemetry/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.compilerOption.json",
"compilerOptions": {
"baseUrl": ".",
"rootDir": "src",
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
"outDir": "dist"
},
"include": ["src"],
}
Loading

0 comments on commit 5cfe770

Please sign in to comment.