convex
Version:
Client for the Convex Cloud
318 lines (314 loc) • 13 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var workos_exports = {};
__export(workos_exports, {
ensureWorkosEnvironmentProvisioned: () => ensureWorkosEnvironmentProvisioned,
provisionWorkosTeamInteractive: () => provisionWorkosTeamInteractive,
tryToCreateAssociatedWorkosTeam: () => tryToCreateAssociatedWorkosTeam
});
module.exports = __toCommonJS(workos_exports);
var import_crypto = __toESM(require("crypto"), 1);
var import_log = require("../../../bundler/log.js");
var import_api = require("../api.js");
var import_env = require("../env.js");
var import_envvars = require("../envvars.js");
var import_prompts = require("../utils/prompts.js");
var import_environmentApi = require("./environmentApi.js");
var import_platformApi = require("./platformApi.js");
async function ensureWorkosEnvironmentProvisioned(ctx, deploymentName, deployment, options) {
if (!options.autoConfigureAuthkitConfig) {
return "choseNotToAssociatedTeam";
}
(0, import_log.showSpinner)("Checking for associated AuthKit environment...");
const existingEnvVars = await getExistingWorkosEnvVars(ctx, deployment);
if (existingEnvVars.clientId && existingEnvVars.environmentId && existingEnvVars.apiKey) {
(0, import_log.logOutput)(
"Deployment already has environment variables for a WorkOS environment configured for AuthKit."
);
await updateEnvLocal(
ctx,
existingEnvVars.clientId,
existingEnvVars.apiKey,
existingEnvVars.environmentId
);
await updateWorkosEnvironment(ctx, existingEnvVars.apiKey);
(0, import_log.logFinishedStep)("WorkOS AutKit environment ready");
return "ready";
}
const response = await (0, import_platformApi.getDeploymentCanProvisionWorkOSEnvironments)(
ctx,
deploymentName
);
const { hasAssociatedWorkosTeam, teamId } = response;
if (response.disabled) {
return "choseNotToAssociatedTeam";
}
if (!hasAssociatedWorkosTeam) {
if (!options.offerToAssociateWorkOSTeam) {
return "choseNotToAssociatedTeam";
}
const result = await tryToCreateAssociatedWorkosTeam(
ctx,
deploymentName,
teamId
);
if (result === "choseNotToAssociatedTeam") {
return "choseNotToAssociatedTeam";
}
result;
}
const environmentResult = await (0, import_platformApi.createEnvironmentAndAPIKey)(
ctx,
deploymentName,
options.environmentName
);
if (!environmentResult.success) {
if (environmentResult.error === "team_not_provisioned") {
return await ctx.crash({
exitCode: 1,
errorType: "fatal",
printedMessage: `Team unexpectedly has no provisioned WorkOS team: ${environmentResult.message}`
});
}
return await ctx.crash({
exitCode: 1,
errorType: "fatal",
printedMessage: environmentResult.message
});
}
const data = environmentResult.data;
if (data.newlyProvisioned) {
(0, import_log.logMessage)("New AuthKit environment provisioned");
} else {
(0, import_log.logMessage)(
"Using credentials from existing AuthKit environment already created for this deployment"
);
}
(0, import_log.changeSpinner)("Setting WORKOS_* deployment environment variables...");
await setConvexEnvVars(
ctx,
deployment,
data.clientId,
data.environmentId,
data.apiKey
);
(0, import_log.showSpinner)("Updating .env.local with WorkOS configuration");
await updateEnvLocal(ctx, data.clientId, data.apiKey, data.environmentId);
await updateWorkosEnvironment(ctx, data.apiKey);
(0, import_log.logFinishedStep)("WorkOS AutKit environment ready");
return "ready";
}
async function provisionWorkosTeamInteractive(ctx, deploymentName, teamId, options = {}) {
const teamInfo = await (0, import_api.getTeamAndProjectSlugForDeployment)(ctx, {
deploymentName
});
if (teamInfo === null) {
return await ctx.crash({
exitCode: 1,
errorType: "fatal",
printedMessage: `Can't find Convex Cloud team for deployment ${deploymentName}`
});
}
(0, import_log.stopSpinner)();
const defaultPrefix = `A WorkOS team needs to be created for your Convex team "${teamInfo.teamSlug}" in order to use AuthKit.
You and other members of this team will be able to create WorkOS environments for each Convex dev deployment for projects in this team.
By creating this account you agree to the WorkOS Terms of Service (https://workos.com/legal/terms-of-service) and Privacy Policy (https://workos.com/legal/privacy).
Alternately, choose no and set WORKOS_CLIENT_ID for an existing WorkOS environment.
`;
const defaultMessage = `Create a WorkOS team and enable automatic AuthKit environment provisioning for team "${teamInfo.teamSlug}"?`;
const agree = await (0, import_prompts.promptYesNo)(ctx, {
prefix: options.promptPrefix ?? defaultPrefix,
message: options.promptMessage ?? defaultMessage
});
if (!agree) {
return { success: false, reason: "cancelled" };
}
const alreadyTried = /* @__PURE__ */ new Map();
let email;
while (true) {
let choice = "refresh";
while (choice === "refresh") {
const { availableEmails } = await (0, import_platformApi.getCandidateEmailsForWorkIntegration)(ctx);
choice = await (0, import_prompts.promptOptions)(ctx, {
message: availableEmails.length === 1 ? "Create a new WorkOS team with this email address?" : "Create a new WorkOS team with which email address?",
suffix: availableEmails.length === 0 ? "\nVisit https://dashboard.convex.dev/profile to add a verified email to use to provision a WorkOS account" : availableEmails.length === 1 ? "\nCreate a new WorkOS team with this email address?" : "\nTo use another email address visit https://dashboard.convex.dev/profile to add and verify, then choose 'refresh'",
choices: [
...availableEmails.map((email2) => ({
name: `${email2}${alreadyTried.has(email2) ? ` (can't create, a WorkOS team already exists with this email)` : ""}`,
value: email2
})),
{
name: "refresh (add an email at https://dashboard.convex.dev/profile)",
value: "refresh"
},
{
name: "cancel (do not create a WorkOS account)",
value: "cancel"
}
]
});
}
if (choice === "cancel") {
return { success: false, reason: "cancelled" };
}
email = choice;
const teamResult = await (0, import_platformApi.createAssociatedWorkosTeam)(ctx, teamId, email);
if (teamResult.result === "emailAlreadyUsed") {
(0, import_log.logMessage)(teamResult.message);
alreadyTried.set(email, teamResult.message);
continue;
}
return {
success: true,
workosTeamId: teamResult.workosTeamId,
workosTeamName: teamResult.workosTeamName
};
}
}
async function tryToCreateAssociatedWorkosTeam(ctx, deploymentName, teamId) {
const result = await provisionWorkosTeamInteractive(
ctx,
deploymentName,
teamId
);
if (!result.success) {
return "choseNotToAssociatedTeam";
}
(0, import_log.logFinishedStep)("WorkOS team created successfully");
return "ready";
}
async function updateWorkosEnvironment(ctx, workosApiKey) {
let { frontendDevUrl } = await (0, import_envvars.suggestedEnvVarName)(ctx);
frontendDevUrl = frontendDevUrl || "http://localhost:5173";
const redirectUri = `${frontendDevUrl}/callback`;
const corsOrigin = `${frontendDevUrl}`;
await applyConfigToWorkosEnvironment(ctx, {
workosApiKey,
redirectUri,
corsOrigin
});
}
async function applyConfigToWorkosEnvironment(ctx, {
workosApiKey,
redirectUri,
corsOrigin
}) {
(0, import_log.changeSpinner)("Configuring AuthKit redirect URI...");
const { modified: redirectUriAdded } = await (0, import_environmentApi.createRedirectURI)(
ctx,
workosApiKey,
redirectUri
);
if (redirectUriAdded) {
(0, import_log.logMessage)(`AuthKit redirect URI added: ${redirectUri}`);
}
(0, import_log.changeSpinner)("Configuring AuthKit CORS origin...");
const { modified: corsAdded } = await (0, import_environmentApi.createCORSOrigin)(
ctx,
workosApiKey,
corsOrigin
);
if (corsAdded) {
(0, import_log.logMessage)(`AuthKit CORS origin added: ${corsOrigin}`);
}
}
async function updateEnvLocal(ctx, clientId, apiKey, environmentId) {
const envPath = ".env.local";
const { frontendDevUrl, detectedFramework, publicPrefix } = await (0, import_envvars.suggestedEnvVarName)(ctx);
if (!detectedFramework || !["Vite", "Next.js", "TanStackStart"].includes(detectedFramework)) {
(0, import_log.logWarning)(
"Can't configure .env.local, fill it out according to directions for the corresponding AuthKit SDK. Use `npx convex list` to see relevant environment variables."
);
}
let suggestedChanges = {};
let existingFileContent = ctx.fs.exists(envPath) ? ctx.fs.readUtf8File(envPath) : null;
if (publicPrefix) {
if (detectedFramework === "Vite") {
suggestedChanges[`${publicPrefix}WORKOS_CLIENT_ID`] = {
value: clientId,
commentOnPreviousLine: `# See this environment at ${workosUrl(environmentId, "/authentication")}`
};
} else if (detectedFramework === "Next.js" || detectedFramework === "TanStackStart") {
suggestedChanges[`WORKOS_CLIENT_ID`] = {
value: clientId,
commentOnPreviousLine: `# See this environment at ${workosUrl(environmentId, "/authentication")}`
};
}
if (frontendDevUrl) {
suggestedChanges[detectedFramework === "TanStackStart" ? "WORKOS_REDIRECT_URI" : `${publicPrefix}WORKOS_REDIRECT_URI`] = {
value: `${frontendDevUrl}/callback`
};
}
}
if (detectedFramework === "Next.js" || detectedFramework === "TanStackStart") {
if (!existingFileContent || !existingFileContent.includes("WORKOS_COOKIE_PASSWORD")) {
suggestedChanges["WORKOS_COOKIE_PASSWORD"] = {
value: import_crypto.default.randomBytes(32).toString("base64url")
};
}
suggestedChanges["WORKOS_API_KEY"] = { value: apiKey };
}
for (const [
envVarName,
{ value: envVarValue, commentOnPreviousLine, commentAfterValue }
] of Object.entries(suggestedChanges)) {
existingFileContent = (0, import_envvars.changedEnvVarFile)({
existingFileContent,
envVarName,
envVarValue,
commentAfterValue: commentAfterValue ?? null,
commentOnPreviousLine: commentOnPreviousLine ?? null
}) || existingFileContent;
}
if (existingFileContent !== null) {
ctx.fs.writeUtf8File(envPath, existingFileContent);
(0, import_log.logMessage)(
`Updated .env.local with ${Object.keys(suggestedChanges).join(", ")}`
);
}
}
async function getExistingWorkosEnvVars(ctx, deployment) {
const [clientId, environmentId, apiKey] = await Promise.all([
(0, import_env.envGetInDeployment)(ctx, deployment, "WORKOS_CLIENT_ID"),
(0, import_env.envGetInDeployment)(ctx, deployment, "WORKOS_ENVIRONMENT_ID"),
(0, import_env.envGetInDeployment)(ctx, deployment, "WORKOS_ENVIRONMENT_API_KEY")
]);
return { clientId, environmentId, apiKey };
}
async function setConvexEnvVars(ctx, deployment, workosClientId, workosEnvironmentId, workosEnvironmentApiKey) {
await (0, import_env.callUpdateEnvironmentVariables)(ctx, deployment, [
{ name: "WORKOS_CLIENT_ID", value: workosClientId },
{ name: "WORKOS_ENVIRONMENT_ID", value: workosEnvironmentId },
{ name: "WORKOS_ENVIRONMENT_API_KEY", value: workosEnvironmentApiKey }
]);
}
function workosUrl(environmentId, subpath) {
return `https://dashboard.workos.com/${environmentId}${subpath}`;
}
//# sourceMappingURL=workos.js.map