@convex-dev/cli
Advanced tools
Comparing version 0.0.42 to 0.0.44
{ | ||
"name": "@convex-dev/cli", | ||
"description": "Command-line interface for Convex Cloud backend.", | ||
"version": "0.0.42", | ||
"version": "0.0.44", | ||
"author": "Convex, Inc. <team@convex.dev>", | ||
@@ -17,4 +17,4 @@ "homepage": "https://convex.dev", | ||
"devDependencies": { | ||
"@convex-dev/bundler": "0.0.42", | ||
"@convex-dev/common": "0.0.42", | ||
"@convex-dev/bundler": "0.0.44", | ||
"@convex-dev/common": "0.0.44", | ||
"@types/deep-equal": "1.0.1", | ||
@@ -21,0 +21,0 @@ "@types/node": "^16.11.7", |
@@ -10,3 +10,2 @@ import chalk from "chalk"; | ||
import axiosRetry from "axios-retry"; | ||
import applyCaseMiddleware from "axios-case-converter"; | ||
@@ -22,2 +21,10 @@ export const provisionHost = | ||
/** Type representing auth configuration. */ | ||
export interface AuthInfo { | ||
// Provider-specific application identifier. Corresponds to the `aud` field in an OIDC token. | ||
applicationID: string; | ||
// Domain used for authentication. Corresponds to the `iss` field in an OIDC token. | ||
domain: string; | ||
} | ||
/** Type representing Convex instance configuration. */ | ||
@@ -29,2 +36,3 @@ export interface InstanceConfig { | ||
indexes: Index[]; | ||
authInfo: AuthInfo[]; | ||
} | ||
@@ -43,2 +51,16 @@ | ||
/** Check if object is of AuthInfo type. */ | ||
function isAuthInfo(object: any): object is AuthInfo { | ||
return ( | ||
"applicationID" in object && | ||
typeof object.applicationID === "string" && | ||
"domain" in object && | ||
typeof object.domain === "string" | ||
); | ||
} | ||
function isAuthInfos(object: any): object is AuthInfo[] { | ||
return Array.isArray(object) && object.every((item: any) => isAuthInfo(item)); | ||
} | ||
/** Check if object is of Index[] type. */ | ||
@@ -71,8 +93,14 @@ function isIndexes(object: any): object is Index[] { | ||
} | ||
return { | ||
origin: obj.origin, | ||
instanceName: obj.instanceName, | ||
functions: obj.functions, | ||
indexes: obj.indexes, | ||
}; | ||
// Allow the `authInfo` key to be omitted, treating it as an empty list of providers. | ||
obj.authInfo = obj.authInfo ?? []; | ||
if (!isAuthInfos(obj.authInfo)) { | ||
throw new ParseError("Expected authInfo to be type AuthInfo[]"); | ||
} | ||
// Important! We return the object itself (not a new object) because | ||
// we want to ensure that fields we're unaware of are "passed through". | ||
// It's possible that this is an old client and the server knows about new | ||
// fields that we don't. | ||
return obj; | ||
} | ||
@@ -132,2 +160,6 @@ | ||
export async function readEnv(): Promise<Env> { | ||
if (process.env.CONVEX_ADMIN_KEY) { | ||
return { adminKey: process.env.CONVEX_ADMIN_KEY }; | ||
} | ||
let envContents; | ||
@@ -275,2 +307,3 @@ try { | ||
// If the list of indexes is empty, filter out the key so it won't be serialized into the JSON output. | ||
// There's no need to filter out AuthInfo when unset, as JSON.stringify will skip over values that are `undefined`. | ||
let keys = Object.keys(instanceConfig).filter( | ||
@@ -346,3 +379,2 @@ (key) => !(key == "indexes" && instanceConfig.indexes.length == 0) | ||
}); | ||
adminKey = process.env.CONVEX_ADMIN_KEY || adminKey; | ||
return client | ||
@@ -387,3 +419,2 @@ .post(`${origin}/api/${version}/get_config`, { version, adminKey }) | ||
): Promise<void> { | ||
adminKey = process.env.CONVEX_ADMIN_KEY || adminKey; | ||
const config = configJSON(instanceConfig, modules, adminKey); | ||
@@ -506,121 +537,5 @@ const origin = instanceConfig.origin; | ||
} | ||
// TODO: Consider printing out changes to `InstanceConfig.authInfo` here as well. | ||
return diff; | ||
} | ||
interface ProvisionArgs { | ||
version: string; | ||
email: string; | ||
betaKey?: string; | ||
} | ||
/** Provision a new empty server instance and return the origin. */ | ||
export async function provision( | ||
email: string, | ||
betaKey?: string | ||
): Promise<{ | ||
instanceName: string; | ||
instanceOrigin: string; | ||
adminKey: string; | ||
redemptionsRemaining: number; | ||
}> { | ||
let provisioningArgs: ProvisionArgs = { | ||
version, | ||
email, | ||
betaKey, | ||
}; | ||
const provisionUrl = `${provisionHost}/api/${version}/provision`; | ||
const client = applyCaseMiddleware(axios.create()); | ||
return client | ||
.post(provisionUrl, provisioningArgs) | ||
.then(function (res) { | ||
if (!res.data.success) { | ||
if (res.data.error === "NEED_BETA_KEY") { | ||
console.error( | ||
chalk.red( | ||
"Error: a beta key is required to provision a convex backend." | ||
) | ||
); | ||
console.error( | ||
chalk.gray( | ||
"Pass it with --beta-key; if you don't have one, sign up at https://convex.dev" | ||
) | ||
); | ||
} else if (res.data.error === "KEY_EXHAUSTED") { | ||
console.error( | ||
chalk.red( | ||
"Error: this beta key has already been used the maximum number of times." | ||
) | ||
); | ||
console.error( | ||
chalk.gray("Contact support@convex.dev to request a new key") | ||
); | ||
} else if (res.data.error === "EMAIL_NOT_ASSOCIATED_WITH_KEY") { | ||
console.error( | ||
chalk.red( | ||
"Error: the email provided is not associated with the given beta key." | ||
) | ||
); | ||
console.error( | ||
chalk.gray("Contact support@convex.dev if this message is in error") | ||
); | ||
} else { | ||
console.error( | ||
chalk.red( | ||
"Unknown error during provisioning: " + JSON.stringify(res.data) | ||
) | ||
); | ||
console.error( | ||
chalk.gray("Contact support@convex.dev if issue persists") | ||
); | ||
} | ||
process.exit(1); | ||
} else { | ||
const instanceOrigin = res.data.url; | ||
const instanceName = res.data.instanceName; | ||
const adminKey = res.data.adminKey; | ||
const redemptionsRemaining = res.data.redemptionsRemaining; | ||
if (!adminKey || !instanceOrigin || !redemptionsRemaining) { | ||
console.error( | ||
chalk.red( | ||
"Unknown error during provisioning: " + JSON.stringify(res.data) | ||
) | ||
); | ||
process.exit(1); | ||
} | ||
return { instanceName, instanceOrigin, adminKey, redemptionsRemaining }; | ||
} | ||
}) | ||
.catch(function (err) { | ||
console.error(chalk.red("Error: Unable to provision instance")); | ||
console.error(chalk.gray(err.message)); | ||
if (err.response) { | ||
console.error(chalk.gray(err.response.data)); | ||
} | ||
process.exit(1); | ||
}); | ||
} | ||
export async function dashboardLogin( | ||
instanceOrigin: string, | ||
adminKey: string | ||
): Promise<string> { | ||
return axios | ||
.post(`${instanceOrigin}/api/${version}/one_time_login_url`, { | ||
version, | ||
adminKey, | ||
}) | ||
.then(function (res) { | ||
return res.data.loginUrl; | ||
}) | ||
.catch(function (err) { | ||
console.error( | ||
chalk.red("Error: Unable to login to dashboard at ", instanceOrigin) | ||
); | ||
console.error(chalk.gray(err.message)); | ||
if (err.response) { | ||
console.error(chalk.gray(err.response.data)); | ||
} | ||
process.exit(1); | ||
}); | ||
} |
import { Command } from "commander"; | ||
import { dashboardLogin } from "./config"; | ||
import chalk from "chalk"; | ||
import { readAuth, readConfig } from "./config"; | ||
import open from "open"; | ||
import axios from "axios"; | ||
import { version } from "@convex-dev/common"; | ||
@@ -28,1 +29,25 @@ /** Pull the local config and overwrite the remote one. */ | ||
}); | ||
async function dashboardLogin( | ||
instanceOrigin: string, | ||
adminKey: string | ||
): Promise<string> { | ||
return axios | ||
.post(`${instanceOrigin}/api/${version}/one_time_login_url`, { | ||
version, | ||
adminKey, | ||
}) | ||
.then(function (res) { | ||
return res.data.loginUrl; | ||
}) | ||
.catch(function (err) { | ||
console.error( | ||
chalk.red("Error: Unable to login to dashboard at ", instanceOrigin) | ||
); | ||
console.error(chalk.gray(err.message)); | ||
if (err.response) { | ||
console.error(chalk.gray(err.response.data)); | ||
} | ||
process.exit(1); | ||
}); | ||
} |
@@ -5,3 +5,3 @@ import { Command } from "commander"; | ||
import { | ||
provision, | ||
provisionHost, | ||
pullConfig, | ||
@@ -12,3 +12,2 @@ writeAdminKey, | ||
readConfig, | ||
provisionHost, | ||
} from "./config"; | ||
@@ -15,0 +14,0 @@ import { version } from "@convex-dev/common"; |
@@ -7,2 +7,3 @@ #!/usr/bin/env node | ||
import { deactivate } from "./deactivate"; | ||
import { version } from "@convex-dev/common"; | ||
@@ -14,3 +15,4 @@ const program = new Command(); | ||
program.addCommand(deactivate); | ||
program.version(version); | ||
program.parseAsync(process.argv); |
111
src/init.ts
import { Command } from "commander"; | ||
import chalk from "chalk"; | ||
import fs from "fs"; | ||
import { provision, pullConfig, writeAdminKey, writeConfig } from "./config"; | ||
import { | ||
provisionHost, | ||
pullConfig, | ||
writeAdminKey, | ||
writeConfig, | ||
} from "./config"; | ||
import { version } from "@convex-dev/common"; | ||
import applyCaseMiddleware from "axios-case-converter"; | ||
import axios from "axios"; | ||
@@ -9,3 +17,3 @@ /** Initialize a new Convex project and deploy server instance. */ | ||
.description("Initialize a new Convex app in the current directory") | ||
.option( | ||
.requiredOption( | ||
"--email <address>", | ||
@@ -31,8 +39,2 @@ "Email address to associate with the created instance" | ||
} | ||
if (!options.email) { | ||
console.log( | ||
chalk.red(`Provide an email address with the "--email" flag.`) | ||
); | ||
process.exit(1); | ||
} | ||
const email = options.email; | ||
@@ -100,1 +102,94 @@ const betaKey = options.betaKey; | ||
}); | ||
interface ProvisionArgs { | ||
version: string; | ||
email: string; | ||
betaKey?: string; | ||
} | ||
/** Provision a new empty server instance and return the origin. */ | ||
async function provision( | ||
email: string, | ||
betaKey?: string | ||
): Promise<{ | ||
instanceName: string; | ||
instanceOrigin: string; | ||
adminKey: string; | ||
redemptionsRemaining: number; | ||
}> { | ||
let provisioningArgs: ProvisionArgs = { | ||
version, | ||
email, | ||
betaKey, | ||
}; | ||
const provisionUrl = `${provisionHost}/api/${version}/provision`; | ||
const client = applyCaseMiddleware(axios.create()); | ||
return client | ||
.post(provisionUrl, provisioningArgs) | ||
.then(function (res) { | ||
if (!res.data.success) { | ||
if (res.data.error === "NEED_BETA_KEY") { | ||
console.error( | ||
chalk.red( | ||
"Error: a beta key is required to provision a convex backend." | ||
) | ||
); | ||
console.error( | ||
chalk.gray( | ||
"Pass it with --beta-key; if you don't have one, sign up at https://convex.dev" | ||
) | ||
); | ||
} else if (res.data.error === "KEY_EXHAUSTED") { | ||
console.error( | ||
chalk.red( | ||
"Error: this beta key has already been used the maximum number of times." | ||
) | ||
); | ||
console.error( | ||
chalk.gray("Contact support@convex.dev to request a new key") | ||
); | ||
} else if (res.data.error === "EMAIL_NOT_ASSOCIATED_WITH_KEY") { | ||
console.error( | ||
chalk.red( | ||
"Error: the email provided is not associated with the given beta key." | ||
) | ||
); | ||
console.error( | ||
chalk.gray("Contact support@convex.dev if this message is in error") | ||
); | ||
} else { | ||
console.error( | ||
chalk.red( | ||
"Unknown error during provisioning: " + JSON.stringify(res.data) | ||
) | ||
); | ||
console.error( | ||
chalk.gray("Contact support@convex.dev if issue persists") | ||
); | ||
} | ||
process.exit(1); | ||
} else { | ||
const instanceOrigin = res.data.url; | ||
const instanceName = res.data.instanceName; | ||
const adminKey = res.data.adminKey; | ||
const redemptionsRemaining = res.data.redemptionsRemaining; | ||
if (!adminKey || !instanceOrigin || !redemptionsRemaining) { | ||
console.error( | ||
chalk.red( | ||
"Unknown error during provisioning: " + JSON.stringify(res.data) | ||
) | ||
); | ||
process.exit(1); | ||
} | ||
return { instanceName, instanceOrigin, adminKey, redemptionsRemaining }; | ||
} | ||
}) | ||
.catch(function (err) { | ||
console.error(chalk.red("Error: Unable to provision instance")); | ||
console.error(chalk.gray(err.message)); | ||
if (err.response) { | ||
console.error(chalk.gray(err.response.data)); | ||
} | ||
process.exit(1); | ||
}); | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
426536
16
11433