@coder/backstage-plugin-coder
Version:
Create and manage Coder workspaces from Backstage
1,349 lines (1,339 loc) • 111 kB
JavaScript
import { createRouteRef, createApiRef, useApi, errorApiRef, createPlugin, createApiFactory, discoveryApiRef, configApiRef, identityApiRef, createComponentExtension } from '@backstage/core-plugin-api';
import globalAxios, { isAxiosError, AxiosError } from 'axios';
import 'ua-parser-js';
import React, { useState, useEffect, createContext, useContext, useCallback, useRef, useLayoutEffect, Component, useMemo, forwardRef } from 'react';
import { ValiError, optional, union, literal, undefined_, object, string, record, parse } from 'valibot';
import { scmIntegrationsApiRef } from '@backstage/integration-react';
import { useEntity, getEntitySourceLocation } from '@backstage/plugin-catalog-react';
import { createPortal } from 'react-dom';
import { useQuery, useQueryClient, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { makeStyles } from '@material-ui/core';
import { LinkButton, Link } from '@backstage/core-components';
import Dialog from '@material-ui/core/Dialog';
import DialogContent from '@material-ui/core/DialogContent';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogActions from '@material-ui/core/DialogActions';
import TextField from '@material-ui/core/TextField';
import ErrorIcon from '@material-ui/icons/ErrorOutline';
import SyncIcon from '@material-ui/icons/Sync';
const rootRouteRef = createRouteRef({
id: "coder"
});
const CODER_API_REF_ID_PREFIX = "backstage-plugin-coder";
const DEFAULT_TANSTACK_QUERY_RETRY_COUNT = 3;
var __defProp$3 = Object.defineProperty;
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$3 = (obj, key, value) => {
__defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
function areSameByReference(v1, v2) {
return Object.is(v1, v2) || v1 === 0 && v2 === 0;
}
function defaultDidSnapshotsChange(oldSnapshot, newSnapshot) {
if (areSameByReference(oldSnapshot, newSnapshot)) {
return false;
}
const oldIsPrimitive = typeof oldSnapshot !== "object" || oldSnapshot === null;
const newIsPrimitive = typeof newSnapshot !== "object" || newSnapshot === null;
if (oldIsPrimitive && newIsPrimitive) {
const numbersAreWithinTolerance = typeof oldSnapshot === "number" && typeof newSnapshot === "number" && Math.abs(oldSnapshot - newSnapshot) < 5e-5;
if (numbersAreWithinTolerance) {
return false;
}
return oldSnapshot !== newSnapshot;
}
const changedFromObjectToPrimitive = !oldIsPrimitive && newIsPrimitive;
const changedFromPrimitiveToObject = oldIsPrimitive && !newIsPrimitive;
if (changedFromObjectToPrimitive || changedFromPrimitiveToObject) {
return true;
}
if (Array.isArray(oldSnapshot) && Array.isArray(newSnapshot)) {
const sameByShallowComparison = oldSnapshot.length === newSnapshot.length && oldSnapshot.every(
(element, index) => areSameByReference(element, newSnapshot[index])
);
return !sameByShallowComparison;
}
const oldInnerValues = Object.values(oldSnapshot);
const newInnerValues = Object.values(newSnapshot);
if (oldInnerValues.length !== newInnerValues.length) {
return true;
}
for (const [index, value] of oldInnerValues.entries()) {
if (value !== newInnerValues[index]) {
return true;
}
}
return false;
}
class StateSnapshotManager {
constructor(options) {
__publicField$3(this, "subscriptions");
__publicField$3(this, "didSnapshotsChange");
__publicField$3(this, "activeSnapshot");
__publicField$3(this, "unsubscribe", (callback) => {
this.subscriptions.delete(callback);
});
__publicField$3(this, "subscribe", (callback) => {
this.subscriptions.add(callback);
return () => this.unsubscribe(callback);
});
__publicField$3(this, "getSnapshot", () => {
return this.activeSnapshot;
});
__publicField$3(this, "updateSnapshot", (newSnapshot) => {
const snapshotsChanged = this.didSnapshotsChange(
this.activeSnapshot,
newSnapshot
);
if (!snapshotsChanged) {
return;
}
this.activeSnapshot = newSnapshot;
this.notifySubscriptions();
});
const { initialSnapshot, didSnapshotsChange } = options;
this.subscriptions = /* @__PURE__ */ new Set();
this.activeSnapshot = initialSnapshot;
this.didSnapshotsChange = didSnapshotsChange != null ? didSnapshotsChange : defaultDidSnapshotsChange;
}
notifySubscriptions() {
const snapshotBinding = this.activeSnapshot;
this.subscriptions.forEach((cb) => cb(snapshotBinding));
}
}
var __defProp$2 = Object.defineProperty;
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$2 = (obj, key, value) => {
__defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
const CODER_PROXY_PREFIX = "/coder";
const BASE_URL_KEY_FOR_CONFIG_API = "backend.baseUrl";
const PROXY_URL_KEY_FOR_DISCOVERY_API = "proxy";
const defaultUrlPrefixes = {
proxyPrefix: `/api/proxy`
};
const proxyRouteReplacer = /\/api\/proxy.*?$/;
class UrlSync {
constructor(setup) {
__publicField$2(this, "discoveryApi");
__publicField$2(this, "urlCache");
__publicField$2(this, "urlPrefixes");
/* ***************************************************************************
* All public functions should be defined as arrow functions to ensure they
* can be passed around React without risk of losing their `this` context
****************************************************************************/
__publicField$2(this, "getApiEndpoint", async () => {
const proxyRoot = await this.discoveryApi.getBaseUrl(
PROXY_URL_KEY_FOR_DISCOVERY_API
);
const newSnapshot = this.prepareNewSnapshot(proxyRoot);
this.urlCache.updateSnapshot(newSnapshot);
return newSnapshot.apiRoute;
});
__publicField$2(this, "getAssetsEndpoint", async () => {
const proxyRoot = await this.discoveryApi.getBaseUrl(
PROXY_URL_KEY_FOR_DISCOVERY_API
);
const newSnapshot = this.prepareNewSnapshot(proxyRoot);
this.urlCache.updateSnapshot(newSnapshot);
return newSnapshot.assetsRoute;
});
__publicField$2(this, "getCachedUrls", () => {
return this.urlCache.getSnapshot();
});
__publicField$2(this, "unsubscribe", (callback) => {
this.urlCache.unsubscribe(callback);
});
__publicField$2(this, "subscribe", (callback) => {
this.urlCache.subscribe(callback);
return () => this.unsubscribe(callback);
});
const { apis, urlPrefixes = {} } = setup;
const { discoveryApi, configApi } = apis;
this.discoveryApi = discoveryApi;
this.urlPrefixes = { ...defaultUrlPrefixes, ...urlPrefixes };
const proxyRoot = this.getProxyRootFromConfigApi(configApi);
this.urlCache = new StateSnapshotManager({
initialSnapshot: this.prepareNewSnapshot(proxyRoot)
});
}
// ConfigApi is literally only used because it offers a synchronous way to
// get an initial URL to use from inside the constructor. Should not be used
// beyond initial constructor call, so it's not being embedded in the class
getProxyRootFromConfigApi(configApi) {
const baseUrl = configApi.getString(BASE_URL_KEY_FOR_CONFIG_API);
return `${baseUrl}${this.urlPrefixes.proxyPrefix}`;
}
prepareNewSnapshot(newProxyUrl) {
return {
baseUrl: newProxyUrl.replace(proxyRouteReplacer, ""),
assetsRoute: `${newProxyUrl}${CODER_PROXY_PREFIX}`,
apiRoute: `${newProxyUrl}${CODER_PROXY_PREFIX}`
};
}
}
const urlSyncApiRef = createApiRef({
id: `${CODER_API_REF_ID_PREFIX}.url-sync`
});
const delay = (ms) => new Promise((res) => {
setTimeout(res, ms);
});
const FeatureNames = [
"access_control",
"advanced_template_scheduling",
"appearance",
"audit_log",
"browser_only",
"control_shared_ports",
"custom_roles",
"external_provisioner_daemons",
"external_token_encryption",
"high_availability",
"multiple_external_auth",
"scim",
"template_rbac",
"user_limit",
"user_role_management",
"workspace_batch_actions",
"workspace_proxy"
];
var __defProp$1 = Object.defineProperty;
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$1 = (obj, key, value) => {
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
const getMissingParameters = (oldBuildParameters, newBuildParameters, templateParameters) => {
const missingParameters = [];
const requiredParameters = [];
templateParameters.forEach((p) => {
const isMutableAndRequired = p.mutable && p.required;
const isImmutable = !p.mutable;
if (isMutableAndRequired || isImmutable) {
requiredParameters.push(p);
}
});
for (const parameter of requiredParameters) {
let buildParameter = newBuildParameters.find(
(p) => p.name === parameter.name
);
if (!buildParameter) {
buildParameter = oldBuildParameters.find((p) => p.name === parameter.name);
}
if (buildParameter) {
continue;
}
missingParameters.push(parameter);
}
templateParameters.forEach((templateParameter) => {
if (templateParameter.options.length === 0) {
return;
}
let buildParameter = newBuildParameters.find(
(p) => p.name === templateParameter.name
);
if (!buildParameter) {
buildParameter = oldBuildParameters.find(
(p) => p.name === templateParameter.name
);
}
if (!buildParameter) {
return;
}
const matchingOption = templateParameter.options.find(
(option) => option.value === (buildParameter == null ? void 0 : buildParameter.value)
);
if (!matchingOption) {
missingParameters.push(templateParameter);
}
});
return missingParameters;
};
const getURLWithSearchParams = (basePath, options) => {
if (!options) {
return basePath;
}
const searchParams = new URLSearchParams();
const keys = Object.keys(options);
keys.forEach((key) => {
const value = options[key];
if (value !== void 0 && value !== "") {
searchParams.append(key, value.toString());
}
});
const searchString = searchParams.toString();
return searchString ? `${basePath}?${searchString}` : basePath;
};
const withDefaultFeatures = (fs) => {
for (const feature of FeatureNames) {
if (fs[feature] !== void 0) {
continue;
}
fs[feature] = {
enabled: false,
entitlement: "not_entitled"
};
}
return fs;
};
const BASE_CONTENT_TYPE_JSON = {
"Content-Type": "application/json"
};
class MissingBuildParameters extends Error {
constructor(parameters, versionId) {
super("Missing build parameters.");
__publicField$1(this, "parameters", []);
__publicField$1(this, "versionId");
this.parameters = parameters;
this.versionId = versionId;
}
}
class ApiMethods {
constructor(axios) {
this.axios = axios;
__publicField$1(this, "login", async (email, password) => {
const payload = JSON.stringify({ email, password });
const response = await this.axios.post(
"/api/v2/users/login",
payload,
{ headers: { ...BASE_CONTENT_TYPE_JSON } }
);
return response.data;
});
__publicField$1(this, "convertToOAUTH", async (request) => {
const response = await this.axios.post(
"/api/v2/users/me/convert-login",
request
);
return response.data;
});
__publicField$1(this, "logout", async () => {
return this.axios.post("/api/v2/users/logout");
});
__publicField$1(this, "getAuthenticatedUser", async () => {
const response = await this.axios.get("/api/v2/users/me");
return response.data;
});
__publicField$1(this, "getUserParameters", async (templateID) => {
const response = await this.axios.get(
`/api/v2/users/me/autofill-parameters?template_id=${templateID}`
);
return response.data;
});
__publicField$1(this, "getAuthMethods", async () => {
const response = await this.axios.get(
"/api/v2/users/authmethods"
);
return response.data;
});
__publicField$1(this, "getUserLoginType", async () => {
const response = await this.axios.get(
"/api/v2/users/me/login-type"
);
return response.data;
});
__publicField$1(this, "checkAuthorization", async (params) => {
const response = await this.axios.post(
`/api/v2/authcheck`,
params
);
return response.data;
});
__publicField$1(this, "getApiKey", async () => {
const response = await this.axios.post(
"/api/v2/users/me/keys"
);
return response.data;
});
__publicField$1(this, "getTokens", async (params) => {
const response = await this.axios.get(
`/api/v2/users/me/keys/tokens`,
{ params }
);
return response.data;
});
__publicField$1(this, "deleteToken", async (keyId) => {
await this.axios.delete(`/api/v2/users/me/keys/${keyId}`);
});
__publicField$1(this, "createToken", async (params) => {
const response = await this.axios.post(
`/api/v2/users/me/keys/tokens`,
params
);
return response.data;
});
__publicField$1(this, "getTokenConfig", async () => {
const response = await this.axios.get(
"/api/v2/users/me/keys/tokens/tokenconfig"
);
return response.data;
});
__publicField$1(this, "getUsers", async (options, signal) => {
const url = getURLWithSearchParams("/api/v2/users", options);
const response = await this.axios.get(
url.toString(),
{ signal }
);
return response.data;
});
__publicField$1(this, "getOrganization", async (organizationId) => {
const response = await this.axios.get(
`/api/v2/organizations/${organizationId}`
);
return response.data;
});
__publicField$1(this, "getOrganizations", async () => {
const response = await this.axios.get(
"/api/v2/users/me/organizations"
);
return response.data;
});
__publicField$1(this, "getTemplate", async (templateId) => {
const response = await this.axios.get(
`/api/v2/templates/${templateId}`
);
return response.data;
});
__publicField$1(this, "getTemplates", async (organizationId, options) => {
const params = {};
if ((options == null ? void 0 : options.deprecated) !== void 0) {
params.deprecated = String(options.deprecated);
}
const response = await this.axios.get(
`/api/v2/organizations/${organizationId}/templates`,
{ params }
);
return response.data;
});
__publicField$1(this, "getTemplateByName", async (organizationId, name) => {
const response = await this.axios.get(
`/api/v2/organizations/${organizationId}/templates/${name}`
);
return response.data;
});
__publicField$1(this, "getTemplateVersion", async (versionId) => {
const response = await this.axios.get(
`/api/v2/templateversions/${versionId}`
);
return response.data;
});
__publicField$1(this, "getTemplateVersionResources", async (versionId) => {
const response = await this.axios.get(
`/api/v2/templateversions/${versionId}/resources`
);
return response.data;
});
__publicField$1(this, "getTemplateVersionVariables", async (versionId) => {
const response = await this.axios.get(
`/api/v2/templateversions/${versionId}/variables`
);
return response.data;
});
__publicField$1(this, "getTemplateVersions", async (templateId) => {
const response = await this.axios.get(
`/api/v2/templates/${templateId}/versions`
);
return response.data;
});
__publicField$1(this, "getTemplateVersionByName", async (organizationId, templateName, versionName) => {
const response = await this.axios.get(
`/api/v2/organizations/${organizationId}/templates/${templateName}/versions/${versionName}`
);
return response.data;
});
__publicField$1(this, "getPreviousTemplateVersionByName", async (organizationId, templateName, versionName) => {
try {
const response = await this.axios.get(
`/api/v2/organizations/${organizationId}/templates/${templateName}/versions/${versionName}/previous`
);
return response.data;
} catch (error) {
const is404 = isAxiosError(error) && error.response && error.response.status === 404;
if (is404) {
return void 0;
}
throw error;
}
});
__publicField$1(this, "createTemplateVersion", async (organizationId, data) => {
const response = await this.axios.post(
`/api/v2/organizations/${organizationId}/templateversions`,
data
);
return response.data;
});
__publicField$1(this, "getTemplateVersionExternalAuth", async (versionId) => {
const response = await this.axios.get(
`/api/v2/templateversions/${versionId}/external-auth`
);
return response.data;
});
__publicField$1(this, "getTemplateVersionRichParameters", async (versionId) => {
const response = await this.axios.get(
`/api/v2/templateversions/${versionId}/rich-parameters`
);
return response.data;
});
__publicField$1(this, "createTemplate", async (organizationId, data) => {
const response = await this.axios.post(
`/api/v2/organizations/${organizationId}/templates`,
data
);
return response.data;
});
__publicField$1(this, "updateActiveTemplateVersion", async (templateId, data) => {
const response = await this.axios.patch(
`/api/v2/templates/${templateId}/versions`,
data
);
return response.data;
});
__publicField$1(this, "patchTemplateVersion", async (templateVersionId, data) => {
const response = await this.axios.patch(
`/api/v2/templateversions/${templateVersionId}`,
data
);
return response.data;
});
__publicField$1(this, "archiveTemplateVersion", async (templateVersionId) => {
const response = await this.axios.post(
`/api/v2/templateversions/${templateVersionId}/archive`
);
return response.data;
});
__publicField$1(this, "unarchiveTemplateVersion", async (templateVersionId) => {
const response = await this.axios.post(
`/api/v2/templateversions/${templateVersionId}/unarchive`
);
return response.data;
});
__publicField$1(this, "updateTemplateMeta", async (templateId, data) => {
const response = await this.axios.patch(
`/api/v2/templates/${templateId}`,
data
);
if (response.status === 304) {
return null;
}
return response.data;
});
__publicField$1(this, "deleteTemplate", async (templateId) => {
const response = await this.axios.delete(
`/api/v2/templates/${templateId}`
);
return response.data;
});
__publicField$1(this, "getWorkspace", async (workspaceId, params) => {
const response = await this.axios.get(
`/api/v2/workspaces/${workspaceId}`,
{ params }
);
return response.data;
});
__publicField$1(this, "getWorkspaces", async (options) => {
const url = getURLWithSearchParams("/api/v2/workspaces", options);
const response = await this.axios.get(url);
return response.data;
});
__publicField$1(this, "getWorkspaceByOwnerAndName", async (username = "me", workspaceName, params) => {
const response = await this.axios.get(
`/api/v2/users/${username}/workspace/${workspaceName}`,
{ params }
);
return response.data;
});
__publicField$1(this, "getWorkspaceBuildByNumber", async (username = "me", workspaceName, buildNumber) => {
const response = await this.axios.get(
`/api/v2/users/${username}/workspace/${workspaceName}/builds/${buildNumber}`
);
return response.data;
});
__publicField$1(this, "waitForBuild", (build) => {
return new Promise((res, reject) => {
void (async () => {
let latestJobInfo = void 0;
while (
// eslint-disable-next-line no-loop-func -- Not great, but should be harmless
!["succeeded", "canceled"].some(
(status) => latestJobInfo == null ? void 0 : latestJobInfo.status.includes(status)
)
) {
const { job } = await this.getWorkspaceBuildByNumber(
build.workspace_owner_name,
build.workspace_name,
build.build_number
);
latestJobInfo = job;
if (latestJobInfo.status === "failed") {
return reject(latestJobInfo);
}
await delay(1e3);
}
return res(latestJobInfo);
})();
});
});
__publicField$1(this, "postWorkspaceBuild", async (workspaceId, data) => {
const response = await this.axios.post(
`/api/v2/workspaces/${workspaceId}/builds`,
data
);
return response.data;
});
__publicField$1(this, "startWorkspace", (workspaceId, templateVersionId, logLevel, buildParameters) => {
return this.postWorkspaceBuild(workspaceId, {
transition: "start",
template_version_id: templateVersionId,
log_level: logLevel,
rich_parameter_values: buildParameters
});
});
__publicField$1(this, "stopWorkspace", (workspaceId, logLevel) => {
return this.postWorkspaceBuild(workspaceId, {
transition: "stop",
log_level: logLevel
});
});
__publicField$1(this, "deleteWorkspace", (workspaceId, options) => {
return this.postWorkspaceBuild(workspaceId, {
transition: "delete",
...options
});
});
__publicField$1(this, "cancelWorkspaceBuild", async (workspaceBuildId) => {
const response = await this.axios.patch(
`/api/v2/workspacebuilds/${workspaceBuildId}/cancel`
);
return response.data;
});
__publicField$1(this, "updateWorkspaceDormancy", async (workspaceId, dormant) => {
const data = { dormant };
const response = await this.axios.put(
`/api/v2/workspaces/${workspaceId}/dormant`,
data
);
return response.data;
});
__publicField$1(this, "updateWorkspaceAutomaticUpdates", async (workspaceId, automaticUpdates) => {
const req = {
automatic_updates: automaticUpdates
};
const response = await this.axios.put(
`/api/v2/workspaces/${workspaceId}/autoupdates`,
req
);
return response.data;
});
__publicField$1(this, "restartWorkspace", async ({
workspace,
buildParameters
}) => {
const stopBuild = await this.stopWorkspace(workspace.id);
const awaitedStopBuild = await this.waitForBuild(stopBuild);
if ((awaitedStopBuild == null ? void 0 : awaitedStopBuild.status) === "canceled") {
return;
}
const startBuild = await this.startWorkspace(
workspace.id,
workspace.latest_build.template_version_id,
void 0,
buildParameters
);
await this.waitForBuild(startBuild);
});
__publicField$1(this, "cancelTemplateVersionBuild", async (templateVersionId) => {
const response = await this.axios.patch(
`/api/v2/templateversions/${templateVersionId}/cancel`
);
return response.data;
});
__publicField$1(this, "createUser", async (user) => {
const response = await this.axios.post(
"/api/v2/users",
user
);
return response.data;
});
__publicField$1(this, "createWorkspace", async (organizationId, userId = "me", workspace) => {
const response = await this.axios.post(
`/api/v2/organizations/${organizationId}/members/${userId}/workspaces`,
workspace
);
return response.data;
});
__publicField$1(this, "patchWorkspace", async (workspaceId, data) => {
await this.axios.patch(`/api/v2/workspaces/${workspaceId}`, data);
});
__publicField$1(this, "getBuildInfo", async () => {
const response = await this.axios.get("/api/v2/buildinfo");
return response.data;
});
__publicField$1(this, "getUpdateCheck", async () => {
const response = await this.axios.get("/api/v2/updatecheck");
return response.data;
});
__publicField$1(this, "putWorkspaceAutostart", async (workspaceID, autostart) => {
const payload = JSON.stringify(autostart);
await this.axios.put(
`/api/v2/workspaces/${workspaceID}/autostart`,
payload,
{ headers: { ...BASE_CONTENT_TYPE_JSON } }
);
});
__publicField$1(this, "putWorkspaceAutostop", async (workspaceID, ttl) => {
const payload = JSON.stringify(ttl);
await this.axios.put(`/api/v2/workspaces/${workspaceID}/ttl`, payload, {
headers: { ...BASE_CONTENT_TYPE_JSON }
});
});
__publicField$1(this, "updateProfile", async (userId, data) => {
const response = await this.axios.put(
`/api/v2/users/${userId}/profile`,
data
);
return response.data;
});
__publicField$1(this, "updateAppearanceSettings", async (userId, data) => {
const response = await this.axios.put(
`/api/v2/users/${userId}/appearance`,
data
);
return response.data;
});
__publicField$1(this, "getUserQuietHoursSchedule", async (userId) => {
const response = await this.axios.get(
`/api/v2/users/${userId}/quiet-hours`
);
return response.data;
});
__publicField$1(this, "updateUserQuietHoursSchedule", async (userId, data) => {
const response = await this.axios.put(
`/api/v2/users/${userId}/quiet-hours`,
data
);
return response.data;
});
__publicField$1(this, "activateUser", async (userId) => {
const response = await this.axios.put(
`/api/v2/users/${userId}/status/activate`
);
return response.data;
});
__publicField$1(this, "suspendUser", async (userId) => {
const response = await this.axios.put(
`/api/v2/users/${userId}/status/suspend`
);
return response.data;
});
__publicField$1(this, "deleteUser", async (userId) => {
await this.axios.delete(`/api/v2/users/${userId}`);
});
// API definition:
// https://github.com/coder/coder/blob/db665e7261f3c24a272ccec48233a3e276878239/coderd/users.go#L33-L53
__publicField$1(this, "hasFirstUser", async () => {
var _a;
try {
await this.axios.get("/api/v2/users/first");
return true;
} catch (error) {
if (isAxiosError(error) && ((_a = error.response) == null ? void 0 : _a.status) === 404) {
return false;
}
throw error;
}
});
__publicField$1(this, "createFirstUser", async (req) => {
const response = await this.axios.post(`/api/v2/users/first`, req);
return response.data;
});
__publicField$1(this, "updateUserPassword", async (userId, updatePassword) => {
await this.axios.put(`/api/v2/users/${userId}/password`, updatePassword);
});
__publicField$1(this, "getRoles", async () => {
const response = await this.axios.get(
`/api/v2/users/roles`
);
return response.data;
});
__publicField$1(this, "updateUserRoles", async (roles, userId) => {
const response = await this.axios.put(
`/api/v2/users/${userId}/roles`,
{ roles }
);
return response.data;
});
__publicField$1(this, "getUserSSHKey", async (userId = "me") => {
const response = await this.axios.get(
`/api/v2/users/${userId}/gitsshkey`
);
return response.data;
});
__publicField$1(this, "regenerateUserSSHKey", async (userId = "me") => {
const response = await this.axios.put(
`/api/v2/users/${userId}/gitsshkey`
);
return response.data;
});
__publicField$1(this, "getWorkspaceBuilds", async (workspaceId, req) => {
const response = await this.axios.get(
getURLWithSearchParams(`/api/v2/workspaces/${workspaceId}/builds`, req)
);
return response.data;
});
__publicField$1(this, "getWorkspaceBuildLogs", async (buildId, before) => {
const response = await this.axios.get(
`/api/v2/workspacebuilds/${buildId}/logs?before=${before.getTime()}`
);
return response.data;
});
__publicField$1(this, "getWorkspaceAgentLogs", async (agentID) => {
const response = await this.axios.get(
`/api/v2/workspaceagents/${agentID}/logs`
);
return response.data;
});
__publicField$1(this, "putWorkspaceExtension", async (workspaceId, newDeadline) => {
await this.axios.put(`/api/v2/workspaces/${workspaceId}/extend`, {
deadline: newDeadline
});
});
__publicField$1(this, "refreshEntitlements", async () => {
await this.axios.post("/api/v2/licenses/refresh-entitlements");
});
__publicField$1(this, "getEntitlements", async () => {
var _a;
try {
const response = await this.axios.get(
"/api/v2/entitlements"
);
return response.data;
} catch (ex) {
if (isAxiosError(ex) && ((_a = ex.response) == null ? void 0 : _a.status) === 404) {
return {
errors: [],
features: withDefaultFeatures({}),
has_license: false,
require_telemetry: false,
trial: false,
warnings: [],
refreshed_at: ""
};
}
throw ex;
}
});
__publicField$1(this, "getExperiments", async () => {
var _a;
try {
const response = await this.axios.get(
"/api/v2/experiments"
);
return response.data;
} catch (error) {
if (isAxiosError(error) && ((_a = error.response) == null ? void 0 : _a.status) === 404) {
return [];
}
throw error;
}
});
__publicField$1(this, "getAvailableExperiments", async () => {
var _a;
try {
const response = await this.axios.get("/api/v2/experiments/available");
return response.data;
} catch (error) {
if (isAxiosError(error) && ((_a = error.response) == null ? void 0 : _a.status) === 404) {
return { safe: [] };
}
throw error;
}
});
__publicField$1(this, "getExternalAuthProvider", async (provider) => {
const res = await this.axios.get(`/api/v2/external-auth/${provider}`);
return res.data;
});
__publicField$1(this, "getExternalAuthDevice", async (provider) => {
const resp = await this.axios.get(
`/api/v2/external-auth/${provider}/device`
);
return resp.data;
});
__publicField$1(this, "exchangeExternalAuthDevice", async (provider, req) => {
const resp = await this.axios.post(
`/api/v2/external-auth/${provider}/device`,
req
);
return resp.data;
});
__publicField$1(this, "getUserExternalAuthProviders", async () => {
const resp = await this.axios.get(`/api/v2/external-auth`);
return resp.data;
});
__publicField$1(this, "unlinkExternalAuthProvider", async (provider) => {
const resp = await this.axios.delete(`/api/v2/external-auth/${provider}`);
return resp.data;
});
__publicField$1(this, "getOAuth2ProviderApps", async (filter) => {
const params = (filter == null ? void 0 : filter.user_id) ? new URLSearchParams({ user_id: filter.user_id }).toString() : "";
const resp = await this.axios.get(`/api/v2/oauth2-provider/apps?${params}`);
return resp.data;
});
__publicField$1(this, "getOAuth2ProviderApp", async (id) => {
const resp = await this.axios.get(`/api/v2/oauth2-provider/apps/${id}`);
return resp.data;
});
__publicField$1(this, "postOAuth2ProviderApp", async (data) => {
const response = await this.axios.post(
`/api/v2/oauth2-provider/apps`,
data
);
return response.data;
});
__publicField$1(this, "putOAuth2ProviderApp", async (id, data) => {
const response = await this.axios.put(
`/api/v2/oauth2-provider/apps/${id}`,
data
);
return response.data;
});
__publicField$1(this, "deleteOAuth2ProviderApp", async (id) => {
await this.axios.delete(`/api/v2/oauth2-provider/apps/${id}`);
});
__publicField$1(this, "getOAuth2ProviderAppSecrets", async (id) => {
const resp = await this.axios.get(
`/api/v2/oauth2-provider/apps/${id}/secrets`
);
return resp.data;
});
__publicField$1(this, "postOAuth2ProviderAppSecret", async (id) => {
const resp = await this.axios.post(
`/api/v2/oauth2-provider/apps/${id}/secrets`
);
return resp.data;
});
__publicField$1(this, "deleteOAuth2ProviderAppSecret", async (appId, secretId) => {
await this.axios.delete(
`/api/v2/oauth2-provider/apps/${appId}/secrets/${secretId}`
);
});
__publicField$1(this, "revokeOAuth2ProviderApp", async (appId) => {
await this.axios.delete(`/oauth2/tokens?client_id=${appId}`);
});
__publicField$1(this, "getAuditLogs", async (options) => {
const url = getURLWithSearchParams("/api/v2/audit", options);
const response = await this.axios.get(url);
return response.data;
});
__publicField$1(this, "getTemplateDAUs", async (templateId) => {
const response = await this.axios.get(
`/api/v2/templates/${templateId}/daus`
);
return response.data;
});
__publicField$1(this, "getDeploymentDAUs", async (offset = Math.trunc((/* @__PURE__ */ new Date()).getTimezoneOffset() / 60)) => {
const response = await this.axios.get(
`/api/v2/insights/daus?tz_offset=${offset}`
);
return response.data;
});
__publicField$1(this, "getTemplateACLAvailable", async (templateId, options) => {
const url = getURLWithSearchParams(
`/api/v2/templates/${templateId}/acl/available`,
options
).toString();
const response = await this.axios.get(url);
return response.data;
});
__publicField$1(this, "getTemplateACL", async (templateId) => {
const response = await this.axios.get(
`/api/v2/templates/${templateId}/acl`
);
return response.data;
});
__publicField$1(this, "updateTemplateACL", async (templateId, data) => {
const response = await this.axios.patch(
`/api/v2/templates/${templateId}/acl`,
data
);
return response.data;
});
__publicField$1(this, "getApplicationsHost", async () => {
const response = await this.axios.get(`/api/v2/applications/host`);
return response.data;
});
__publicField$1(this, "getGroups", async (organizationId) => {
const response = await this.axios.get(
`/api/v2/organizations/${organizationId}/groups`
);
return response.data;
});
__publicField$1(this, "createGroup", async (organizationId, data) => {
const response = await this.axios.post(
`/api/v2/organizations/${organizationId}/groups`,
data
);
return response.data;
});
__publicField$1(this, "getGroup", async (groupId) => {
const response = await this.axios.get(`/api/v2/groups/${groupId}`);
return response.data;
});
__publicField$1(this, "patchGroup", async (groupId, data) => {
const response = await this.axios.patch(`/api/v2/groups/${groupId}`, data);
return response.data;
});
__publicField$1(this, "addMember", async (groupId, userId) => {
return this.patchGroup(groupId, {
name: "",
add_users: [userId],
remove_users: []
});
});
__publicField$1(this, "removeMember", async (groupId, userId) => {
return this.patchGroup(groupId, {
name: "",
display_name: "",
add_users: [],
remove_users: [userId]
});
});
__publicField$1(this, "deleteGroup", async (groupId) => {
await this.axios.delete(`/api/v2/groups/${groupId}`);
});
__publicField$1(this, "getWorkspaceQuota", async (username) => {
const response = await this.axios.get(
`/api/v2/workspace-quota/${encodeURIComponent(username)}`
);
return response.data;
});
__publicField$1(this, "getAgentListeningPorts", async (agentID) => {
const response = await this.axios.get(
`/api/v2/workspaceagents/${agentID}/listening-ports`
);
return response.data;
});
__publicField$1(this, "getWorkspaceAgentSharedPorts", async (workspaceID) => {
const response = await this.axios.get(
`/api/v2/workspaces/${workspaceID}/port-share`
);
return response.data;
});
__publicField$1(this, "upsertWorkspaceAgentSharedPort", async (workspaceID, req) => {
const response = await this.axios.post(
`/api/v2/workspaces/${workspaceID}/port-share`,
req
);
return response.data;
});
__publicField$1(this, "deleteWorkspaceAgentSharedPort", async (workspaceID, req) => {
const response = await this.axios.delete(
`/api/v2/workspaces/${workspaceID}/port-share`,
{ data: req }
);
return response.data;
});
// getDeploymentSSHConfig is used by the VSCode-Extension.
__publicField$1(this, "getDeploymentSSHConfig", async () => {
const response = await this.axios.get(`/api/v2/deployment/ssh`);
return response.data;
});
__publicField$1(this, "getDeploymentConfig", async () => {
const response = await this.axios.get(`/api/v2/deployment/config`);
return response.data;
});
__publicField$1(this, "getDeploymentStats", async () => {
const response = await this.axios.get(`/api/v2/deployment/stats`);
return response.data;
});
__publicField$1(this, "getReplicas", async () => {
const response = await this.axios.get(`/api/v2/replicas`);
return response.data;
});
__publicField$1(this, "getFile", async (fileId) => {
const response = await this.axios.get(
`/api/v2/files/${fileId}`,
{ responseType: "arraybuffer" }
);
return response.data;
});
__publicField$1(this, "getWorkspaceProxyRegions", async () => {
const response = await this.axios.get(`/api/v2/regions`);
return response.data;
});
__publicField$1(this, "getWorkspaceProxies", async () => {
const response = await this.axios.get(`/api/v2/workspaceproxies`);
return response.data;
});
__publicField$1(this, "createWorkspaceProxy", async (b) => {
const response = await this.axios.post(`/api/v2/workspaceproxies`, b);
return response.data;
});
__publicField$1(this, "getAppearance", async () => {
var _a;
try {
const response = await this.axios.get(`/api/v2/appearance`);
return response.data || {};
} catch (ex) {
if (isAxiosError(ex) && ((_a = ex.response) == null ? void 0 : _a.status) === 404) {
return {
application_name: "",
logo_url: "",
notification_banners: [],
service_banner: {
enabled: false
}
};
}
throw ex;
}
});
__publicField$1(this, "updateAppearance", async (b) => {
const response = await this.axios.put(`/api/v2/appearance`, b);
return response.data;
});
__publicField$1(this, "getTemplateExamples", async (organizationId) => {
const response = await this.axios.get(
`/api/v2/organizations/${organizationId}/templates/examples`
);
return response.data;
});
__publicField$1(this, "uploadFile", async (file) => {
const response = await this.axios.post("/api/v2/files", file, {
headers: { "Content-Type": "application/x-tar" }
});
return response.data;
});
__publicField$1(this, "getTemplateVersionLogs", async (versionId) => {
const response = await this.axios.get(
`/api/v2/templateversions/${versionId}/logs`
);
return response.data;
});
__publicField$1(this, "updateWorkspaceVersion", async (workspace) => {
const template = await this.getTemplate(workspace.template_id);
return this.startWorkspace(workspace.id, template.active_version_id);
});
__publicField$1(this, "getWorkspaceBuildParameters", async (workspaceBuildId) => {
const response = await this.axios.get(
`/api/v2/workspacebuilds/${workspaceBuildId}/parameters`
);
return response.data;
});
__publicField$1(this, "getLicenses", async () => {
const response = await this.axios.get(`/api/v2/licenses`);
return response.data;
});
__publicField$1(this, "createLicense", async (data) => {
const response = await this.axios.post(`/api/v2/licenses`, data);
return response.data;
});
__publicField$1(this, "removeLicense", async (licenseId) => {
await this.axios.delete(`/api/v2/licenses/${licenseId}`);
});
/** Steps to change the workspace version
* - Get the latest template to access the latest active version
* - Get the current build parameters
* - Get the template parameters
* - Update the build parameters and check if there are missed parameters for
* the new version
* - If there are missing parameters raise an error
* - Create a build with the version and updated build parameters
*/
__publicField$1(this, "changeWorkspaceVersion", async (workspace, templateVersionId, newBuildParameters = []) => {
const [currentBuildParameters, templateParameters] = await Promise.all([
this.getWorkspaceBuildParameters(workspace.latest_build.id),
this.getTemplateVersionRichParameters(templateVersionId)
]);
const missingParameters = getMissingParameters(
currentBuildParameters,
newBuildParameters,
templateParameters
);
if (missingParameters.length > 0) {
throw new MissingBuildParameters(missingParameters, templateVersionId);
}
return this.postWorkspaceBuild(workspace.id, {
transition: "start",
template_version_id: templateVersionId,
rich_parameter_values: newBuildParameters
});
});
/** Steps to update the workspace
* - Get the latest template to access the latest active version
* - Get the current build parameters
* - Get the template parameters
* - Update the build parameters and check if there are missed parameters for
* the newest version
* - If there are missing parameters raise an error
* - Create a build with the latest version and updated build parameters
*/
__publicField$1(this, "updateWorkspace", async (workspace, newBuildParameters = []) => {
const [template, oldBuildParameters] = await Promise.all([
this.getTemplate(workspace.template_id),
this.getWorkspaceBuildParameters(workspace.latest_build.id)
]);
const activeVersionId = template.active_version_id;
const templateParameters = await this.getTemplateVersionRichParameters(
activeVersionId
);
const missingParameters = getMissingParameters(
oldBuildParameters,
newBuildParameters,
templateParameters
);
if (missingParameters.length > 0) {
throw new MissingBuildParameters(missingParameters, activeVersionId);
}
return this.postWorkspaceBuild(workspace.id, {
transition: "start",
template_version_id: activeVersionId,
rich_parameter_values: newBuildParameters
});
});
__publicField$1(this, "getWorkspaceResolveAutostart", async (workspaceId) => {
const response = await this.axios.get(
`/api/v2/workspaces/${workspaceId}/resolve-autostart`
);
return response.data;
});
__publicField$1(this, "issueReconnectingPTYSignedToken", async (params) => {
const response = await this.axios.post(
"/api/v2/applications/reconnecting-pty-signed-token",
params
);
return response.data;
});
__publicField$1(this, "getWorkspaceParameters", async (workspace) => {
const latestBuild = workspace.latest_build;
const [templateVersionRichParameters, buildParameters] = await Promise.all([
this.getTemplateVersionRichParameters(latestBuild.template_version_id),
this.getWorkspaceBuildParameters(latestBuild.id)
]);
return {
templateVersionRichParameters,
buildParameters
};
});
__publicField$1(this, "getInsightsUserLatency", async (filters) => {
const params = new URLSearchParams(filters);
const response = await this.axios.get(
`/api/v2/insights/user-latency?${params}`
);
return response.data;
});
__publicField$1(this, "getInsightsUserActivity", async (filters) => {
const params = new URLSearchParams(filters);
const response = await this.axios.get(
`/api/v2/insights/user-activity?${params}`
);
return response.data;
});
__publicField$1(this, "getInsightsTemplate", async (params) => {
const searchParams = new URLSearchParams(params);
const response = await this.axios.get(
`/api/v2/insights/templates?${searchParams}`
);
return response.data;
});
__publicField$1(this, "getHealth", async (force = false) => {
const params = new URLSearchParams({ force: force.toString() });
const response = await this.axios.get(
`/api/v2/debug/health?${params}`
);
return response.data;
});
__publicField$1(this, "getHealthSettings", async () => {
const res = await this.axios.get(
`/api/v2/debug/health/settings`
);
return res.data;
});
__publicField$1(this, "updateHealthSettings", async (data) => {
const response = await this.axios.put(
`/api/v2/debug/health/settings`,
data
);
return response.data;
});
__publicField$1(this, "putFavoriteWorkspace", async (workspaceID) => {
await this.axios.put(`/api/v2/workspaces/${workspaceID}/favorite`);
});
__publicField$1(this, "deleteFavoriteWorkspace", async (workspaceID) => {
await this.axios.delete(`/api/v2/workspaces/${workspaceID}/favorite`);
});
__publicField$1(this, "getJFrogXRayScan", async (options) => {
var _a;
const searchParams = new URLSearchParams({
workspace_id: options.workspaceId,
agent_id: options.agentId
});
try {
const res = await this.axios.get(
`/api/v2/integrations/jfrog/xray-scan?${searchParams}`
);
return res.data;
} catch (error) {
if (isAxiosError(error) && ((_a = error.response) == null ? void 0 : _a.status) === 404) {
return null;
}
throw error;
}
});
}
}
const csrfToken = "KNKvagCBEHZK7ihe2t7fj6VeJ0UyTDco1yVUJE8N06oNqxLu5Zx1vRxZbgfC0mJJgeGkVjgs08mgPbcWPBkZ1A==";
const tokenMetadataElement = typeof document !== "undefined" ? document.head.querySelector('meta[property="csrf-token"]') : null;
function getConfiguredAxiosInstance() {
var _a;
const instance = globalAxios.create();
instance.defaults.validateStatus = (status) => {
return status >= 200 && status < 300 || status === 304;
};
const metadataIsAvailable = tokenMetadataElement !== null && tokenMetadataElement.getAttribute("content") !== null;
if (metadataIsAvailable) {
if (process.env.NODE_ENV === "development") {
instance.defaults.headers.common["X-CSRF-TOKEN"] = csrfToken;
instance.defaults.headers.common["X-CSRF-TOKEN"] = csrfToken;
tokenMetadataElement.setAttribute("content", csrfToken);
} else {
instance.defaults.headers.common["X-CSRF-TOKEN"] = (_a = tokenMetadataElement.getAttribute("content")) != null ? _a : "";
}
} else {
if (process.env.JEST_WORKER_ID === void 0) {
console.error("CSRF token not found");
}
}
return instance;
}
class Api extends ApiMethods {
constructor() {
const scopedAxiosInstance = getConfiguredAxiosInstance();
super(scopedAxiosInstance);
// As with ApiMethods, all public methods should be defined with arrow
// function syntax to ensure they can be passed around the React UI without
// losing/detaching their `this` context!
__publicField$1(this, "getCsrfToken", () => {
return csrfToken;
});
__publicField$1(this, "setSessionToken", (token) => {
this.axios.defaults.headers.common["Coder-Session-Token"] = token;
});
__publicField$1(this, "setHost", (host) => {
this.axios.defaults.baseURL = host;
});
__publicField$1(this, "getAxiosInstance", () => {
return this.axios;
});
}
}
new Api();
function createCoderApi() {
const api = new Api();
return api;
}
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);