convex
Version:
Client for the Convex Cloud
398 lines (397 loc) • 12.4 kB
JavaScript
;
import chalk from "chalk";
import { logError, logFailure } from "../../bundler/context.js";
import {
deploymentNameFromAdminKeyOrCrash,
deploymentTypeFromAdminKey,
getConfiguredDeploymentFromEnvVar,
getTeamAndProjectFromPreviewAdminKey,
isPreviewDeployKey
} from "./deployment.js";
import { buildEnvironment } from "./envvars.js";
import { checkAuthorization, performLogin } from "./login.js";
import {
CONVEX_DEPLOY_KEY_ENV_VAR_NAME,
bigBrainAPI,
bigBrainAPIMaybeThrows,
getAuthHeaderForBigBrain,
getConfiguredDeploymentName,
getConfiguredDeploymentOrCrash,
logAndHandleFetchError,
readAdminKeyFromEnvVar
} from "./utils.js";
export async function createProjectProvisioningDevOrProd(ctx, {
teamSlug: selectedTeamSlug,
projectName
}, firstDeploymentType) {
const provisioningArgs = {
team: selectedTeamSlug,
projectName,
deploymentType: firstDeploymentType,
backendVersionOverride: process.env.CONVEX_BACKEND_VERSION_OVERRIDE
};
const data = await bigBrainAPI({
ctx,
method: "POST",
url: "create_project",
data: provisioningArgs
});
const {
projectSlug,
teamSlug,
deploymentName,
adminKey,
projectsRemaining,
prodUrl: url
} = data;
if (projectSlug === void 0 || teamSlug === void 0 || deploymentName === void 0 || url === void 0 || adminKey === void 0 || projectsRemaining === void 0) {
const error = "Unexpected response during provisioning: " + JSON.stringify(data);
logError(ctx, chalk.red(error));
return await ctx.crash(1, "transient", error);
}
return {
projectSlug,
teamSlug,
deploymentName,
url,
adminKey,
projectsRemaining
};
}
export async function fetchDeploymentCredentialsProvisioningDevOrProd(ctx, { teamSlug, projectSlug }, deploymentType) {
try {
return fetchDeploymentCredentialsProvisioningDevOrProdMaybeThrows(
ctx,
{ teamSlug, projectSlug },
deploymentType
);
} catch (error) {
return await logAndHandleFetchError(ctx, error);
}
}
export async function fetchDeploymentCredentialsForName(ctx, deploymentName, deploymentType) {
let data;
try {
data = await bigBrainAPIMaybeThrows({
ctx,
method: "POST",
url: "deployment/authorize_for_name",
data: {
deploymentName,
deploymentType
}
});
} catch (error) {
return { error };
}
const adminKey = data.adminKey;
const url = data.url;
const resultDeploymentType = data.deploymentType;
if (adminKey === void 0 || url === void 0) {
const msg = "Unknown error during authorization: " + JSON.stringify(data);
logError(ctx, chalk.red(msg));
return await ctx.crash(1, "transient", new Error(msg));
}
return {
deploymentName,
adminKey,
url,
deploymentType: resultDeploymentType
};
}
export function storeAdminKeyEnvVar(adminKeyOption) {
if (adminKeyOption) {
process.env[CONVEX_DEPLOY_KEY_ENV_VAR_NAME] = adminKeyOption;
}
}
export function deploymentSelectionFromOptions(options) {
storeAdminKeyEnvVar(options.adminKey);
const adminKey = readAdminKeyFromEnvVar();
if (options.url !== void 0) {
if (adminKey) {
return { kind: "urlWithAdminKey", url: options.url, adminKey };
}
return { kind: "urlWithLogin", url: options.url };
}
if (options.previewName !== void 0) {
return { kind: "previewName", previewName: options.previewName };
}
if (options.deploymentName !== void 0) {
return { kind: "deploymentName", deploymentName: options.deploymentName };
}
if (adminKey !== void 0) {
return { kind: "deployKey" };
}
return { kind: options.prod === true ? "ownProd" : "ownDev" };
}
export async function fetchDeploymentCredentialsWithinCurrentProject(ctx, deploymentSelection) {
if (deploymentSelection.kind === "urlWithAdminKey") {
return {
adminKey: deploymentSelection.adminKey,
url: deploymentSelection.url
};
}
const configuredAdminKey = readAdminKeyFromEnvVar();
if (configuredAdminKey === void 0) {
const buildEnvironmentExpectsConvexDeployKey = buildEnvironment();
if (buildEnvironmentExpectsConvexDeployKey) {
logFailure(
ctx,
`${buildEnvironmentExpectsConvexDeployKey} build environment detected but ${CONVEX_DEPLOY_KEY_ENV_VAR_NAME} is not set. Set this environment variable to deploy from this environment. See https://docs.convex.dev/production/hosting`
);
await ctx.crash(1);
}
const header = await getAuthHeaderForBigBrain(ctx);
if (!header) {
logFailure(
ctx,
`Error: You are not logged in. Log in with \`npx convex dev\` or set the ${CONVEX_DEPLOY_KEY_ENV_VAR_NAME} environment variable. See https://docs.convex.dev/production/hosting`
);
await ctx.crash(1);
}
const configuredDeployment = await getConfiguredDeploymentName(ctx);
if (configuredDeployment === null) {
logFailure(
ctx,
"No CONVEX_DEPLOYMENT set, run `npx convex dev` to configure a Convex project"
);
await ctx.crash(1);
}
}
const data = await fetchDeploymentCredentialsWithinCurrentProjectInner(
ctx,
deploymentSelection,
configuredAdminKey
);
const { deploymentName, adminKey, deploymentType, url } = data;
if (adminKey === void 0 || url === void 0 || deploymentName === void 0) {
const msg = "Unknown error during authorization: " + JSON.stringify(data);
logError(ctx, chalk.red(msg));
return await ctx.crash(1, "transient", new Error(msg));
}
return {
deploymentName,
adminKey,
url,
deploymentType
};
}
export async function projectSelection(ctx, configuredDeployment, configuredAdminKey) {
if (configuredAdminKey !== void 0 && isPreviewDeployKey(configuredAdminKey)) {
const { teamSlug, projectSlug } = await getTeamAndProjectFromPreviewAdminKey(ctx, configuredAdminKey);
return {
kind: "teamAndProjectSlugs",
teamSlug,
projectSlug
};
}
if (configuredAdminKey !== void 0) {
return {
kind: "deploymentName",
deploymentName: await deploymentNameFromAdminKeyOrCrash(
ctx,
configuredAdminKey
)
};
}
if (configuredDeployment) {
return {
kind: "deploymentName",
deploymentName: configuredDeployment
};
}
logFailure(
ctx,
"Select project by setting `CONVEX_DEPLOYMENT` with `npx convex dev` or `CONVEX_DEPLOY_KEY` from the Convex dashboard."
);
return await ctx.crash(1);
}
async function fetchDeploymentCredentialsWithinCurrentProjectInner(ctx, deploymentSelection, configuredAdminKey) {
const configuredDeployment = getConfiguredDeploymentFromEnvVar().name;
switch (deploymentSelection.kind) {
case "ownDev": {
return {
...await fetchExistingDevDeploymentCredentialsOrCrash(
ctx,
configuredDeployment
),
deploymentName: configuredDeployment
};
}
case "ownProd":
return await bigBrainAPI({
ctx,
method: "POST",
url: "deployment/authorize_prod",
data: {
deploymentName: configuredDeployment
}
});
case "previewName":
return await bigBrainAPI({
ctx,
method: "POST",
url: "deployment/authorize_preview",
data: {
previewName: deploymentSelection.previewName,
projectSelection: await projectSelection(
ctx,
configuredDeployment,
configuredAdminKey
)
}
});
case "deploymentName":
return await bigBrainAPI({
ctx,
method: "POST",
url: "deployment/authorize_within_current_project",
data: {
selectedDeploymentName: deploymentSelection.deploymentName,
projectSelection: await projectSelection(
ctx,
configuredDeployment,
configuredAdminKey
)
}
});
case "deployKey": {
const deploymentName = await deploymentNameFromAdminKeyOrCrash(
ctx,
configuredAdminKey
);
let url = await deriveUrlFromAdminKey(ctx, configuredAdminKey);
if (process.env.CONVEX_PROVISION_HOST !== void 0) {
url = await bigBrainAPI({
ctx,
method: "POST",
url: "deployment/url_for_key",
data: {
deployKey: configuredAdminKey
}
});
}
const deploymentType = deploymentTypeFromAdminKey(configuredAdminKey);
return {
adminKey: configuredAdminKey,
url,
deploymentName,
deploymentType
};
}
case "urlWithLogin":
return {
...await bigBrainAPI({
ctx,
method: "POST",
url: "deployment/authorize_within_current_project",
data: {
selectedDeploymentName: configuredDeployment,
projectSelection: await projectSelection(
ctx,
configuredDeployment,
configuredAdminKey
)
}
}),
url: deploymentSelection.url
};
default: {
const _exhaustivenessCheck = deploymentSelection;
return ctx.crash(1);
}
}
}
export async function fetchDeploymentCredentialsProvisionProd(ctx, deploymentSelection) {
if (deploymentSelection.kind === "ownDev" && !await checkAuthorization(ctx, false)) {
await performLogin(ctx);
}
if (deploymentSelection.kind !== "ownDev") {
const result2 = await fetchDeploymentCredentialsWithinCurrentProject(
ctx,
deploymentSelection
);
return {
url: result2.url,
adminKey: result2.adminKey,
deploymentName: result2.deploymentName,
deploymentType: result2.deploymentType
};
}
const configuredDeployment = await getConfiguredDeploymentOrCrash(ctx);
const result = await fetchExistingDevDeploymentCredentialsOrCrash(
ctx,
configuredDeployment
);
return {
url: result.url,
adminKey: result.adminKey,
deploymentType: result.deploymentType,
deploymentName: configuredDeployment
};
}
export async function fetchTeamAndProject(ctx, deploymentName) {
const data = await bigBrainAPI({
ctx,
method: "GET",
url: `deployment/${deploymentName}/team_and_project`
});
const { team, project } = data;
if (team === void 0 || project === void 0) {
const msg = "Unknown error when fetching team and project: " + JSON.stringify(data);
logFailure(ctx, msg);
return await ctx.crash(1, "transient", new Error(msg));
}
return data;
}
export async function fetchDeploymentCredentialsProvisioningDevOrProdMaybeThrows(ctx, { teamSlug, projectSlug }, deploymentType) {
const data = await bigBrainAPIMaybeThrows({
ctx,
method: "POST",
url: "deployment/provision_and_authorize",
data: {
teamSlug,
projectSlug,
deploymentType
}
});
const deploymentName = data.deploymentName;
const adminKey = data.adminKey;
const url = data.url;
if (adminKey === void 0 || url === void 0) {
const msg = "Unknown error during authorization: " + JSON.stringify(data);
logError(ctx, chalk.red(msg));
return await ctx.crash(1, "transient", new Error(msg));
}
return { adminKey, url, deploymentName };
}
function credentialsAsDevCredentials(cred) {
if (cred.deploymentType === "dev") {
return cred;
}
throw new Error("Credentials are not for a dev deployment.");
}
async function fetchExistingDevDeploymentCredentialsOrCrash(ctx, deploymentName) {
const credentials = await fetchDeploymentCredentialsForName(
ctx,
deploymentName,
"dev"
);
if ("error" in credentials) {
logFailure(
ctx,
`Failed to authorize "${deploymentName}" configured in CONVEX_DEPLOYMENT, run \`npx convex dev\` to configure a Convex project`
);
return await ctx.crash(1, "invalid filesystem data", credentials.error);
}
if (credentials.deploymentType !== "dev") {
logFailure(ctx, `Deployment "${deploymentName}" is not a dev deployment`);
return await ctx.crash(1, "invalid filesystem data");
}
return credentialsAsDevCredentials(credentials);
}
async function deriveUrlFromAdminKey(ctx, adminKey) {
const deploymentName = await deploymentNameFromAdminKeyOrCrash(ctx, adminKey);
return `https://${deploymentName}.convex.cloud`;
}
//# sourceMappingURL=api.js.map