convex
Version:
Client for the Convex Cloud
298 lines (297 loc) • 8.85 kB
JavaScript
;
import chalk from "chalk";
import inquirer from "inquirer";
import {
logError,
logFailure,
logMessage
} from "../bundler/context.js";
import {
fetchDeploymentCredentialsForName,
fetchDeploymentCredentialsProvisioningDevOrProdMaybeThrows
} from "./lib/api.js";
import {
enforceDeprecatedConfigField,
readProjectConfig,
upgradeOldAuthInfoToAuthConfig,
writeProjectConfig
} from "./lib/config.js";
import {
eraseDeploymentEnvVar,
writeDeploymentEnvVar
} from "./lib/deployment.js";
import { init } from "./lib/init.js";
import { reinit } from "./lib/reinit.js";
import {
functionsDir,
getConfiguredDeploymentName,
hasProject,
hasProjects,
hasTeam,
logAndHandleFetchError,
ThrowingFetchError
} from "./lib/utils.js";
import { writeConvexUrlToEnvFile } from "./lib/envvars.js";
export async function initOrReinitForDeprecatedCommands(ctx, cmdOptions) {
const { url } = await deploymentCredentialsOrConfigure(ctx, null, {
...cmdOptions,
prod: false
});
const envVarWrite = await writeConvexUrlToEnvFile(ctx, url);
if (envVarWrite !== null) {
logMessage(
ctx,
chalk.green(
`Saved the dev deployment URL as ${envVarWrite.envVar} to ${envVarWrite.envFile}`
)
);
}
}
export async function deploymentCredentialsOrConfigure(ctx, chosenConfiguration, cmdOptions) {
const { url, adminKey } = cmdOptions;
if (url !== void 0 && adminKey !== void 0) {
const didErase = await eraseDeploymentEnvVar(ctx);
if (didErase) {
logMessage(
ctx,
chalk.yellowBright(
`Removed the CONVEX_DEPLOYMENT environment variable from .env.local`
)
);
}
const envVarWrite = await writeConvexUrlToEnvFile(ctx, url);
if (envVarWrite !== null) {
logMessage(
ctx,
chalk.green(
`Saved the given --url as ${envVarWrite.envVar} to ${envVarWrite.envFile}`
)
);
}
return { url, adminKey };
}
const deploymentType = cmdOptions.prod ? "prod" : "dev";
const configuredDeployment = chosenConfiguration === null ? await getConfiguredDeploymentOrUpgrade(ctx, deploymentType) : null;
if (configuredDeployment === null) {
const choice2 = chosenConfiguration !== "ask" && chosenConfiguration !== null ? chosenConfiguration : await askToConfigure(ctx);
return await initOrReinit(ctx, choice2, deploymentType, cmdOptions);
}
if ("error" in configuredDeployment) {
const projectConfig = (await readProjectConfig(ctx)).projectConfig;
const choice2 = await askToReconfigure(
ctx,
projectConfig,
configuredDeployment.error
);
return initOrReinit(ctx, choice2, deploymentType, cmdOptions);
}
const { deploymentName } = configuredDeployment;
const adminKeyAndUrlForConfiguredDeployment = await fetchDeploymentCredentialsForName(
ctx,
deploymentName,
deploymentType
);
if (!("error" in adminKeyAndUrlForConfiguredDeployment)) {
return adminKeyAndUrlForConfiguredDeployment;
}
await checkForDeploymentTypeError(
ctx,
adminKeyAndUrlForConfiguredDeployment.error,
deploymentType
);
const choice = await askToReconfigureNew(ctx, deploymentName);
return initOrReinit(ctx, choice, deploymentType, cmdOptions);
}
async function checkForDeploymentTypeError(ctx, error, deploymentType) {
let data = null;
if (error instanceof ThrowingFetchError) {
data = error.serverErrorData || null;
}
if (data && "code" in data && data.code === "DeploymentTypeMismatch") {
if (deploymentType === "prod") {
logFailure(
ctx,
"Use `npx convex deploy` to push changes to your production deployment"
);
} else {
logFailure(
ctx,
"CONVEX_DEPLOYMENT is a production deployment, but --prod flag was not specified. Use `npx convex dev --prod` to develop against this production deployment."
);
}
logError(ctx, chalk.red(data.message));
await ctx.crash(1, "invalid filesystem data", error);
}
}
async function getConfiguredDeploymentOrUpgrade(ctx, deploymentType) {
const deploymentName = await getConfiguredDeploymentName(ctx);
if (deploymentName !== null) {
return { deploymentName };
}
return await upgradeOldConfigToDeploymentVar(ctx, deploymentType);
}
async function initOrReinit(ctx, choice, deploymentType, cmdOptions) {
switch (choice) {
case "new":
return await init(ctx, deploymentType, cmdOptions);
case "existing": {
return await reinit(ctx, deploymentType, cmdOptions);
}
default: {
return choice;
}
}
}
async function upgradeOldConfigToDeploymentVar(ctx, deploymentType) {
const { configPath, projectConfig } = await readProjectConfig(ctx);
const { team, project } = projectConfig;
if (typeof team !== "string" || typeof project !== "string") {
return null;
}
let devDeploymentName;
try {
const { deploymentName } = await fetchDeploymentCredentialsProvisioningDevOrProdMaybeThrows(
ctx,
{ teamSlug: team, projectSlug: project },
deploymentType
);
devDeploymentName = deploymentName;
} catch (error) {
return { error };
}
await writeDeploymentEnvVar(ctx, deploymentType, {
team,
project,
deploymentName: devDeploymentName
});
logMessage(
ctx,
chalk.green(
`Saved the ${deploymentType} deployment name as CONVEX_DEPLOYMENT to .env.local`
)
);
const projectConfigWithoutAuthInfo = await upgradeOldAuthInfoToAuthConfig(
ctx,
projectConfig,
functionsDir(configPath, projectConfig)
);
await writeProjectConfig(ctx, projectConfigWithoutAuthInfo, {
deleteIfAllDefault: true
});
return { deploymentName: devDeploymentName };
}
async function askToConfigure(ctx) {
if (!await hasProjects(ctx)) {
return "new";
}
return await promptToInitWithProjects();
}
async function askToReconfigure(ctx, projectConfig, error) {
const team = await enforceDeprecatedConfigField(ctx, projectConfig, "team");
const project = await enforceDeprecatedConfigField(
ctx,
projectConfig,
"project"
);
const [isExistingTeam, existingProject, hasAnyProjects] = await Promise.all([
await hasTeam(ctx, team),
await hasProject(ctx, team, project),
await hasProjects(ctx)
]);
if (isExistingTeam && existingProject) {
return await logAndHandleFetchError(ctx, error);
}
if (isExistingTeam) {
logFailure(
ctx,
`Project ${chalk.bold(project)} does not exist in your team ${chalk.bold(
team
)}, as configured in ${chalk.bold("convex.json")}`
);
} else {
logFailure(
ctx,
`You don't have access to team ${chalk.bold(
team
)}, as configured in ${chalk.bold("convex.json")}`
);
}
if (!hasAnyProjects) {
const { confirmed } = await inquirer.prompt([
{
type: "confirm",
name: "confirmed",
message: `Create a new project?`,
default: true
}
]);
if (!confirmed) {
logFailure(
ctx,
"Run `npx convex dev` in a directory with a valid convex.json."
);
return await ctx.crash(1, "invalid filesystem data");
}
return "new";
}
return await promptToReconfigure();
}
async function askToReconfigureNew(ctx, configuredDeploymentName) {
logFailure(
ctx,
`You don't have access to the project with deployment ${chalk.bold(
configuredDeploymentName
)}, as configured in ${chalk.bold("CONVEX_DEPLOYMENT")}`
);
const hasAnyProjects = await hasProjects(ctx);
if (!hasAnyProjects) {
const { confirmed } = await inquirer.prompt([
{
type: "confirm",
name: "confirmed",
message: `Configure a new project?`,
default: true
}
]);
if (!confirmed) {
logFailure(
ctx,
"Run `npx convex dev` in a directory with a valid CONVEX_DEPLOYMENT set"
);
return await ctx.crash(1, "invalid filesystem data");
}
return "new";
}
return await promptToReconfigure();
}
export async function promptToInitWithProjects() {
const { choice } = await inquirer.prompt([
{
type: "list",
name: "choice",
message: `What would you like to configure?`,
default: "new",
choices: [
{ name: "a new project", value: "new" },
{ name: "an existing project", value: "existing" }
]
}
]);
return choice;
}
export async function promptToReconfigure() {
const { choice } = await inquirer.prompt([
{
type: "list",
name: "choice",
message: `Configure a different project?`,
default: "new",
choices: [
{ name: "create new project", value: "new" },
{ name: "choose an existing project", value: "existing" }
]
}
]);
return choice;
}
//# sourceMappingURL=configure.js.map