UNPKG

@dataroadinc/setup-auth

Version:

CLI tool and programmatic API for automated OAuth setup across cloud platforms

334 lines (333 loc) 15.6 kB
import axios from "axios"; import { SetupAuthError } from "../../error.js"; export class VercelApiClientImpl { constructor(token) { this.apiUrl = "https://api.vercel.com"; this.teamId = null; this.projectName = null; this.token = token; } async request(method, path, data) { try { const response = await axios({ method, url: `${this.apiUrl}${path}`, data, headers: { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json", }, }); return response.data; } catch (error) { if (axios.isAxiosError(error) && error.response) { const apiError = new Error(`Vercel API error: ${error.response.status} - ${JSON.stringify(error.response.data)}`); const errorWithStatus = apiError; errorWithStatus.status = error.response.status; errorWithStatus.message = error.response.data?.error?.message || error.message; throw apiError; } throw error; } } async innerGetTeamId() { if (this.teamId !== null) { return this.teamId; } if (process.env.VERCEL_TEAM_ID) { this.teamId = process.env.VERCEL_TEAM_ID; return this.teamId; } try { const response = await this.request("GET", "/v2/teams"); if (response && response.teams && response.teams.length > 1) { throw new Error("Multiple teams found. Please set VERCEL_TEAM_ID environment variable."); } if (response && response.teams && response.teams.length > 0) { this.teamId = response.teams[0].id; return this.teamId; } this.teamId = null; return null; } catch (error) { console.error("Error getting team ID:", error); return null; } } async getTeamId() { if (this.teamId !== null) { return this.teamId; } const teamId = await this.innerGetTeamId(); if (teamId && teamId !== null && teamId.length > 0) { this.teamId = teamId; return teamId; } throw new SetupAuthError("No team ID found. Please set VERCEL_TEAM_ID environment variable."); } async getProjectName() { if (this.projectName !== null) { return this.projectName; } if (process.env.VERCEL_PROJECT_NAME) { this.projectName = process.env.VERCEL_PROJECT_NAME; return this.projectName; } try { const teamId = await this.getTeamId(); const endpoint = teamId ? `/v9/projects?teamId=${teamId}` : "/v9/projects"; const response = await this.request("GET", endpoint); if (response && response.projects && response.projects.length > 1) { throw new SetupAuthError("Multiple projects found. Please set VERCEL_PROJECT_NAME environment variable."); } if (response && response.projects && response.projects.length > 0) { this.projectName = response.projects[0].name; if (this.projectName) { process.env.VERCEL_PROJECT_NAME = this.projectName; return this.projectName; } } throw new SetupAuthError("No projects found. Please set VERCEL_PROJECT_NAME environment variable."); } catch (error) { throw new SetupAuthError(`Failed to get project name: ${error instanceof Error ? error.message : String(error)}`); } } async getProjectId() { const projectId = process.env.VERCEL_PROJECT_ID; if (!projectId) { throw new SetupAuthError("VERCEL_PROJECT_ID environment variable is required. Please set it in your .env.local file."); } return projectId; } async getProjects() { try { const teamId = await this.getTeamId(); const endpoint = `/v10/projects?teamId=${teamId}`; const response = await this.request("GET", endpoint); if (response && response.projects) { return response.projects.map((project) => ({ id: project.id, name: project.name, accountId: project.accountId, })); } return []; } catch (error) { if (error && typeof error === "object" && "status" in error) { const errorWithStatus = error; if (errorWithStatus.status === 403 && errorWithStatus.message?.includes("scope")) { throw new SetupAuthError("Access token does not have the required team scope permissions.\n" + "Please create a new access token with the correct team scope:\n" + "1. Go to https://vercel.com/account/tokens\n" + "2. Click 'Create Token'\n" + "3. Select your team from the scope dropdown\n" + "4. Set an appropriate expiration date\n" + "5. Update your VERCEL_ACCESS_TOKEN environment variable\n" + "\nFor more details, see: https://vercel.com/docs/rest-api/reference/welcome#creating-an-access-token", { cause: error }); } } console.warn("Failed to get projects via API:", error); return []; } } async getDeployments() { try { const projectId = await this.getProjectId(); const teamId = await this.getTeamId(); const endpoint = `/v6/deployments?projectId=${projectId}&teamId=${teamId}&limit=10`; const response = await this.request("GET", endpoint); if (response && response.deployments) { return response.deployments .filter((deployment) => deployment.url && deployment.state === "READY") .map(deployment => `https://${deployment.url}`); } return []; } catch (error) { if (error && typeof error === "object" && "status" in error) { const errorWithStatus = error; if (errorWithStatus.status === 403 && errorWithStatus.message?.includes("scope")) { throw new SetupAuthError("Access token does not have the required team scope permissions.\n" + "Please create a new access token with the correct team scope:\n" + "1. Go to https://vercel.com/account/tokens\n" + "2. Click 'Create Token'\n" + "3. Select your team from the scope dropdown\n" + "4. Set an appropriate expiration date\n" + "5. Update your VERCEL_ACCESS_TOKEN environment variable\n" + "\nFor more details, see: https://vercel.com/docs/rest-api/reference/welcome#creating-an-access-token", { cause: error }); } } console.warn("Failed to get deployments via API:", error); return []; } } async getProject(name) { try { const projectId = await this.getProjectId(); const teamId = await this.getTeamId(); const endpoint = `/v10/projects/${projectId}?teamId=${teamId}`; const response = await this.request("GET", endpoint); return response; } catch (error) { if (error && typeof error === "object" && "status" in error) { const errorWithStatus = error; if (errorWithStatus.status === 403 && errorWithStatus.message?.includes("scope")) { throw new SetupAuthError("Access token does not have the required team scope permissions.\n" + "Please create a new access token with the correct team scope:\n" + "1. Go to https://vercel.com/account/tokens\n" + "2. Click 'Create Token'\n" + "3. Select your team from the scope dropdown\n" + "4. Set an appropriate expiration date\n" + "5. Update your VERCEL_ACCESS_TOKEN environment variable\n" + "\nFor more details, see: https://vercel.com/docs/rest-api/reference/welcome#creating-an-access-token", { cause: error }); } } console.error(`Error getting project ${name}:`, error); throw error; } } async getEnvVariables() { try { const projectId = await this.getProjectId(); const teamId = await this.getTeamId(); const endpoint = `/v10/projects/${projectId}/env?teamId=${teamId}`; const response = await this.request("GET", endpoint); if (response && response.envs) { return response.envs; } return []; } catch (error) { if (error && typeof error === "object" && "status" in error) { const errorWithStatus = error; if (errorWithStatus.status === 403 && errorWithStatus.message?.includes("scope")) { throw new SetupAuthError("Access token does not have the required team scope permissions.\n" + "Please create a new access token with the correct team scope:\n" + "1. Go to https://vercel.com/account/tokens\n" + "2. Click 'Create Token'\n" + "3. Select your team from the scope dropdown\n" + "4. Set an appropriate expiration date\n" + "5. Update your VERCEL_ACCESS_TOKEN environment variable\n" + "\nFor more details, see: https://vercel.com/docs/rest-api/reference/welcome#creating-an-access-token", { cause: error }); } } console.error("Error getting environment variables:", error); throw error; } } async createEnvVariable(envVar) { try { const projectId = await this.getProjectId(); const teamId = await this.getTeamId(); const endpoint = `/v10/projects/${projectId}/env?teamId=${teamId}`; const response = await this.request("POST", endpoint, { key: envVar.key, value: envVar.value, target: envVar.targets, type: "plain", }); return response; } catch (error) { if (error && typeof error === "object" && "status" in error) { const errorWithStatus = error; if (errorWithStatus.status === 403 && errorWithStatus.message?.includes("scope")) { throw new SetupAuthError("Access token does not have the required team scope permissions.\n" + "Please create a new access token with the correct team scope:\n" + "1. Go to https://vercel.com/account/tokens\n" + "2. Click 'Create Token'\n" + "3. Select your team from the scope dropdown\n" + "4. Set an appropriate expiration date\n" + "5. Update your VERCEL_ACCESS_TOKEN environment variable\n" + "\nFor more details, see: https://vercel.com/docs/rest-api/reference/welcome#creating-an-access-token", { cause: error }); } } console.error(`Error creating environment variable ${envVar.key}:`, error); throw error; } } async updateEnvVariable(envId, envVar) { try { const projectId = await this.getProjectId(); const teamId = await this.getTeamId(); const endpoint = `/v10/projects/${projectId}/env/${envId}?teamId=${teamId}`; const response = await this.request("PATCH", endpoint, { key: envVar.key, value: envVar.value, target: envVar.targets, type: "plain", }); return response; } catch (error) { if (error && typeof error === "object" && "status" in error) { const errorWithStatus = error; if (errorWithStatus.status === 403 && errorWithStatus.message?.includes("scope")) { throw new SetupAuthError("Access token does not have the required team scope permissions.\n" + "Please create a new access token with the correct team scope:\n" + "1. Go to https://vercel.com/account/tokens\n" + "2. Click 'Create Token'\n" + "3. Select your team from the scope dropdown\n" + "4. Set an appropriate expiration date\n" + "5. Update your VERCEL_ACCESS_TOKEN environment variable\n" + "\nFor more details, see: https://vercel.com/docs/rest-api/reference/welcome#creating-an-access-token", { cause: error }); } } console.error(`Error updating environment variable ${envVar.key}:`, error); throw error; } } async removeEnvVariable(envId) { try { const projectId = await this.getProjectId(); const teamId = await this.getTeamId(); const endpoint = `/v10/projects/${projectId}/env/${envId}?teamId=${teamId}`; await this.request("DELETE", endpoint); return; } catch (error) { if (error && typeof error === "object" && "status" in error) { const errorWithStatus = error; if (errorWithStatus.status === 403 && errorWithStatus.message?.includes("scope")) { throw new SetupAuthError("Access token does not have the required team scope permissions.\n" + "Please create a new access token with the correct team scope:\n" + "1. Go to https://vercel.com/account/tokens\n" + "2. Click 'Create Token'\n" + "3. Select your team from the scope dropdown\n" + "4. Set an appropriate expiration date\n" + "5. Update your VERCEL_ACCESS_TOKEN environment variable\n" + "\nFor more details, see: https://vercel.com/docs/rest-api/reference/welcome#creating-an-access-token", { cause: error }); } } console.error("Error removing environment variable:", error); throw error; } } async getToken() { return this.token; } async getTeams() { const response = await this.request("GET", "/v2/teams"); if (response && response.teams) { return response.teams.map((team) => ({ id: team.id, name: team.name, slug: team.slug, })); } return []; } }