UNPKG

convex

Version:

Client for the Convex Cloud

436 lines (435 loc) 13.4 kB
"use strict"; import { logVerbose } from "../../bundler/context.js"; import { fetchTeamAndProjectForKey } from "./api.js"; import { readProjectConfig } from "./config.js"; import { deploymentNameFromAdminKeyOrCrash, deploymentTypeFromAdminKey, getDeploymentTypeFromConfiguredDeployment, isAnonymousDeployment, isPreviewDeployKey, isProjectKey, stripDeploymentTypePrefix } from "./deployment.js"; import { buildEnvironment } from "./envvars.js"; import { readGlobalConfig } from "./utils/globalConfig.js"; import { CONVEX_DEPLOYMENT_ENV_VAR_NAME, CONVEX_DEPLOY_KEY_ENV_VAR_NAME, CONVEX_SELF_HOSTED_ADMIN_KEY_VAR_NAME, CONVEX_SELF_HOSTED_URL_VAR_NAME, ENV_VAR_FILE_PATH, bigBrainAPI } from "./utils/utils.js"; import * as dotenv from "dotenv"; export async function initializeBigBrainAuth(ctx, initialArgs) { if (initialArgs.url !== void 0 && initialArgs.adminKey !== void 0) { ctx._updateBigBrainAuth( getBigBrainAuth(ctx, { previewDeployKey: null, projectKey: null }) ); return; } if (initialArgs.envFile !== void 0) { const existingFile = ctx.fs.exists(initialArgs.envFile) ? ctx.fs.readUtf8File(initialArgs.envFile) : null; if (existingFile === null) { return ctx.crash({ exitCode: 1, errorType: "invalid filesystem or env vars", printedMessage: "env file does not exist" }); } const config = dotenv.parse(existingFile); const deployKey2 = config[CONVEX_DEPLOY_KEY_ENV_VAR_NAME]; if (deployKey2 !== void 0) { const bigBrainAuth = getBigBrainAuth(ctx, { previewDeployKey: isPreviewDeployKey(deployKey2) ? deployKey2 : null, projectKey: isProjectKey(deployKey2) ? deployKey2 : null }); ctx._updateBigBrainAuth(bigBrainAuth); } return; } dotenv.config({ path: ENV_VAR_FILE_PATH }); dotenv.config(); const deployKey = process.env[CONVEX_DEPLOY_KEY_ENV_VAR_NAME]; if (deployKey !== void 0) { const bigBrainAuth = getBigBrainAuth(ctx, { previewDeployKey: isPreviewDeployKey(deployKey) ? deployKey : null, projectKey: isProjectKey(deployKey) ? deployKey : null }); ctx._updateBigBrainAuth(bigBrainAuth); return; } ctx._updateBigBrainAuth( getBigBrainAuth(ctx, { previewDeployKey: null, projectKey: null }) ); return; } export async function updateBigBrainAuthAfterLogin(ctx, accessToken) { const existingAuth = ctx.bigBrainAuth(); if (existingAuth !== null && existingAuth.kind === "projectKey") { logVerbose( ctx, `Ignoring update to big brain auth since project key takes precedence` ); return; } ctx._updateBigBrainAuth({ accessToken, kind: "accessToken", header: `Bearer ${accessToken}` }); } export async function clearBigBrainAuth(ctx) { ctx._updateBigBrainAuth(null); } function getBigBrainAuth(ctx, opts) { if (process.env.CONVEX_OVERRIDE_ACCESS_TOKEN) { return { accessToken: process.env.CONVEX_OVERRIDE_ACCESS_TOKEN, kind: "accessToken", header: `Bearer ${process.env.CONVEX_OVERRIDE_ACCESS_TOKEN}` }; } if (opts.projectKey !== null) { return { header: `Bearer ${opts.projectKey}`, kind: "projectKey", projectKey: opts.projectKey }; } const globalConfig = readGlobalConfig(ctx); if (globalConfig) { return { kind: "accessToken", header: `Bearer ${globalConfig.accessToken}`, accessToken: globalConfig.accessToken }; } if (opts.previewDeployKey !== null) { return { header: `Bearer ${opts.previewDeployKey}`, kind: "previewDeployKey", previewDeployKey: opts.previewDeployKey }; } return null; } export async function getDeploymentSelection(ctx, cliArgs) { const metadata = await _getDeploymentSelection(ctx, cliArgs); logDeploymentSelection(ctx, metadata); return metadata; } function logDeploymentSelection(ctx, selection) { switch (selection.kind) { case "existingDeployment": { logVerbose( ctx, `Existing deployment: ${selection.deploymentToActOn.url} ${selection.deploymentToActOn.source}` ); break; } case "deploymentWithinProject": { logVerbose( ctx, `Deployment within project: ${prettyProjectSelection(selection.targetProject)}` ); break; } case "preview": { logVerbose(ctx, `Preview deploy key`); break; } case "chooseProject": { logVerbose(ctx, `Choose project`); break; } case "anonymous": { logVerbose( ctx, `Anonymous, has selected deployment?: ${selection.deploymentName !== null}` ); break; } default: { const _exhaustivenessCheck = selection; logVerbose(ctx, `Unknown deployment selection`); } } return null; } function prettyProjectSelection(selection) { switch (selection.kind) { case "teamAndProjectSlugs": { return `Team and project slugs: ${selection.teamSlug} ${selection.projectSlug}`; } case "deploymentName": { return `Deployment name: ${selection.deploymentName}`; } case "projectDeployKey": { return `Project deploy key`; } default: { const _exhaustivenessCheck = selection; return `Unknown`; } } } async function _getDeploymentSelection(ctx, cliArgs) { if (cliArgs.url && cliArgs.adminKey) { return { kind: "existingDeployment", deploymentToActOn: { url: cliArgs.url, adminKey: cliArgs.adminKey, deploymentFields: null, source: "cliArgs" } }; } if (cliArgs.envFile) { logVerbose(ctx, `Checking env file: ${cliArgs.envFile}`); const existingFile = ctx.fs.exists(cliArgs.envFile) ? ctx.fs.readUtf8File(cliArgs.envFile) : null; if (existingFile === null) { return ctx.crash({ exitCode: 1, errorType: "invalid filesystem or env vars", printedMessage: "env file does not exist" }); } const config = dotenv.parse(existingFile); const result2 = await getDeploymentSelectionFromEnv( ctx, (name) => config[name] === void 0 || config[name] === "" ? null : config[name] ); if (result2.kind === "unknown") { return ctx.crash({ exitCode: 1, errorType: "invalid filesystem or env vars", printedMessage: `env file \`${cliArgs.envFile}\` did not contain environment variables for a Convex deployment. Expected \`${CONVEX_DEPLOY_KEY_ENV_VAR_NAME}\`, \`${CONVEX_DEPLOYMENT_ENV_VAR_NAME}\`, or both \`${CONVEX_SELF_HOSTED_URL_VAR_NAME}\` and \`${CONVEX_SELF_HOSTED_ADMIN_KEY_VAR_NAME}\` to be set.` }); } return result2.metadata; } dotenv.config({ path: ENV_VAR_FILE_PATH }); dotenv.config(); const result = await getDeploymentSelectionFromEnv(ctx, (name) => { const value = process.env[name]; if (value === void 0 || value === "") { return null; } return value; }); if (result.kind !== "unknown") { return result.metadata; } const { projectConfig } = await readProjectConfig(ctx); if (projectConfig.team !== void 0 && projectConfig.project !== void 0) { return { kind: "deploymentWithinProject", targetProject: { kind: "teamAndProjectSlugs", teamSlug: projectConfig.team, projectSlug: projectConfig.project } }; } const isLoggedIn = ctx.bigBrainAuth() !== null; if (!isLoggedIn && shouldAllowAnonymousDevelopment()) { return { kind: "anonymous", deploymentName: null }; } return { kind: "chooseProject" }; } async function getDeploymentSelectionFromEnv(ctx, getEnv) { const deployKey = getEnv(CONVEX_DEPLOY_KEY_ENV_VAR_NAME); if (deployKey !== null) { const deployKeyType = isPreviewDeployKey(deployKey) ? "preview" : isProjectKey(deployKey) ? "project" : "deployment"; switch (deployKeyType) { case "preview": { return { kind: "success", metadata: { kind: "preview", previewDeployKey: deployKey } }; } case "project": { return { kind: "success", metadata: { kind: "deploymentWithinProject", targetProject: { kind: "projectDeployKey", projectDeployKey: deployKey } } }; } case "deployment": { const deploymentName = await deploymentNameFromAdminKeyOrCrash( ctx, deployKey ); const deploymentType = deploymentTypeFromAdminKey(deployKey); const url = await bigBrainAPI({ ctx, method: "POST", url: "deployment/url_for_key", data: { deployKey } }); const slugs = await fetchTeamAndProjectForKey(ctx, deployKey); return { kind: "success", metadata: { kind: "existingDeployment", deploymentToActOn: { url, adminKey: deployKey, deploymentFields: { deploymentName, deploymentType, teamSlug: slugs.team, projectSlug: slugs.project }, source: "deployKey" } } }; } default: { const _exhaustivenessCheck = deployKeyType; return ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: `Unexpected deploy key type: ${deployKeyType}` }); } } } await checkIfBuildEnvironmentExpectsConvexDeployKey(ctx); const convexDeployment = getEnv(CONVEX_DEPLOYMENT_ENV_VAR_NAME); const selfHostedUrl = getEnv(CONVEX_SELF_HOSTED_URL_VAR_NAME); const selfHostedAdminKey = getEnv(CONVEX_SELF_HOSTED_ADMIN_KEY_VAR_NAME); if (selfHostedUrl !== null && selfHostedAdminKey !== null) { if (convexDeployment !== null) { return await ctx.crash({ exitCode: 1, errorType: "invalid filesystem or env vars", printedMessage: `${CONVEX_DEPLOYMENT_ENV_VAR_NAME} must not be set when ${CONVEX_SELF_HOSTED_URL_VAR_NAME} and ${CONVEX_SELF_HOSTED_ADMIN_KEY_VAR_NAME} are set` }); } return { kind: "success", metadata: { kind: "existingDeployment", deploymentToActOn: { url: selfHostedUrl, adminKey: selfHostedAdminKey, deploymentFields: null, source: "selfHosted" } } }; } if (convexDeployment !== null) { if (selfHostedUrl !== null || selfHostedAdminKey !== null) { return await ctx.crash({ exitCode: 1, errorType: "invalid filesystem or env vars", printedMessage: `${CONVEX_SELF_HOSTED_URL_VAR_NAME} and ${CONVEX_SELF_HOSTED_ADMIN_KEY_VAR_NAME} must not be set when ${CONVEX_DEPLOYMENT_ENV_VAR_NAME} is set` }); } const targetDeploymentType = getDeploymentTypeFromConfiguredDeployment(convexDeployment); const targetDeploymentName = stripDeploymentTypePrefix(convexDeployment); const isAnonymous = isAnonymousDeployment(targetDeploymentName); if (isAnonymous) { if (!shouldAllowAnonymousDevelopment()) { return { kind: "unknown" }; } return { kind: "success", metadata: { kind: "anonymous", deploymentName: targetDeploymentName } }; } return { kind: "success", metadata: { kind: "deploymentWithinProject", targetProject: { kind: "deploymentName", deploymentName: targetDeploymentName, deploymentType: targetDeploymentType } } }; } return { kind: "unknown" }; } async function checkIfBuildEnvironmentExpectsConvexDeployKey(ctx) { const buildEnvironmentExpectsConvexDeployKey = buildEnvironment(); if (buildEnvironmentExpectsConvexDeployKey) { return await ctx.crash({ exitCode: 1, errorType: "fatal", printedMessage: `${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` }); } } export const deploymentNameFromSelection = (selection) => { return deploymentNameAndTypeFromSelection(selection)?.name ?? null; }; export const deploymentNameAndTypeFromSelection = (selection) => { switch (selection.kind) { case "existingDeployment": { return { name: selection.deploymentToActOn.deploymentFields?.deploymentName ?? null, type: selection.deploymentToActOn.deploymentFields?.deploymentType ?? null }; } case "deploymentWithinProject": { return selection.targetProject.kind === "deploymentName" ? { name: selection.targetProject.deploymentName, type: selection.targetProject.deploymentType } : null; } case "preview": { return null; } case "chooseProject": { return null; } case "anonymous": { return null; } } const _exhaustivenessCheck = selection; return null; }; export const shouldAllowAnonymousDevelopment = () => { if (process.env.CONVEX_ALLOW_ANONYMOUS === "false") { return false; } return true; }; //# sourceMappingURL=deploymentSelection.js.map