Files
komodo/frontend/src/util/client.ts
2023-03-14 20:15:27 +00:00

591 lines
16 KiB
TypeScript

import axios from "axios";
import fileDownload from "js-file-download";
import { MONITOR_BASE_URL } from "..";
import {
AwsBuilderConfig,
BasicContainerInfo,
Build,
BuildActionState,
BuildVersionsReponse,
Deployment,
DeploymentActionState,
DeploymentWithContainerState,
DockerContainerStats,
Group,
HistoricalStatsQuery,
Log,
Operation,
Procedure,
Server,
ServerActionState,
ServerWithStatus,
SystemInformation,
SystemStats,
SystemStatsQuery,
SystemStatsRecord,
Update,
UpdateTarget,
User,
UserCredentials,
} from "../types";
import {
BuildVersionsQuery,
CopyBuildBody,
CopyDeploymentBody,
CreateBuildBody,
CreateDeploymentBody,
CreateGroupBody,
CreateProcedureBody,
CreateSecretBody,
CreateServerBody,
LoginOptions,
ModifyUserCreateBuildBody,
ModifyUserCreateServerBody,
ModifyUserEnabledBody,
PermissionsUpdateBody,
RenameDeploymentBody,
UpdateDescriptionBody,
} from "./client_types";
import { generateQuery, QueryObject } from "./helpers";
export class Client {
loginOptions: LoginOptions | undefined;
monitorTitle: string | undefined;
secrets_cache: Record<string, string[]> = {};
github_accounts_cache: Record<string, string[]> = {};
docker_accounts_cache: Record<string, string[]> = {};
server_version_cache: Record<string, string> = {};
constructor(private baseURL: string, public token: string | null) {}
async initialize() {
const [loginOptions, monitorTitle] = await Promise.all([
this.get_login_options(),
this.get_monitor_title(),
]);
this.loginOptions = loginOptions;
this.monitorTitle = monitorTitle;
document.title = monitorTitle;
const params = new URLSearchParams(location.search);
const exchange_token = params.get("token");
if (exchange_token) {
history.replaceState({}, "", MONITOR_BASE_URL);
try {
const jwt = await this.exchange_for_jwt(exchange_token);
this.token = jwt;
localStorage.setItem("access_token", jwt);
} catch (error) {
console.warn(error);
}
}
}
get_login_options(): Promise<LoginOptions> {
return this.get("/auth/options");
}
login_with_github() {
location.replace(`${MONITOR_BASE_URL}/auth/github/login`);
}
login_with_google() {
location.replace(`${MONITOR_BASE_URL}/auth/google/login`);
}
async login(credentials: UserCredentials) {
const jwt: string = await this.post("/auth/local/login", credentials);
this.token = jwt;
localStorage.setItem("access_token", this.token);
return await this.get_user();
}
async signup(credentials: UserCredentials) {
const jwt: string = await this.post("/auth/local/create_user", credentials);
this.token = jwt;
localStorage.setItem("access_token", this.token);
return await this.get_user();
}
logout() {
localStorage.removeItem("access_token");
this.token = null;
}
async get_user(): Promise<User | false> {
if (this.token) {
try {
return await this.get("/api/user");
} catch (error: any) {
this.logout();
return false;
}
} else {
return false;
}
}
get_username(user_id: string): Promise<string> {
return this.get(`/api/username/${user_id}`);
}
// admin only
list_users(): Promise<User[]> {
return this.get("/api/users");
}
// admin only
get_user_by_id(user_id: string): Promise<User> {
return this.get(`/api/user/${user_id}`);
}
exchange_for_jwt(exchange_token: string): Promise<string> {
return this.post("/auth/exchange", { token: exchange_token });
}
get_github_webhook_base_url(): Promise<string> {
return this.get("/api/github_webhook_base_url");
}
update_description(body: UpdateDescriptionBody): Promise<undefined> {
return this.post("/api/update_description", body);
}
get_monitor_title(): Promise<string> {
return this.get("/api/title");
}
// deployment
list_deployments(
query?: QueryObject
): Promise<DeploymentWithContainerState[]> {
return this.get("/api/deployment/list" + generateQuery(query));
}
get_deployment(id: string): Promise<DeploymentWithContainerState> {
return this.get(`/api/deployment/${id}`);
}
get_deployment_action_state(id: string): Promise<DeploymentActionState> {
return this.get(`/api/deployment/${id}/action_state`);
}
get_deployment_container_log(id: string, tail?: number): Promise<Log> {
return this.get(`/api/deployment/${id}/log${generateQuery({ tail })}`);
}
get_deployment_container_stats(id: string): Promise<DockerContainerStats> {
return this.get(`/api/deployment/${id}/stats`);
}
get_deployment_deployed_version(id: string): Promise<string> {
return this.get(`/api/deployment/${id}/deployed_version`);
}
create_deployment(body: CreateDeploymentBody): Promise<Deployment> {
return this.post("/api/deployment/create", body);
}
create_full_deployment(deployment: Deployment): Promise<Deployment> {
return this.post("/api/deployment/create_full", deployment);
}
copy_deployment(
target_id: string,
body: CopyDeploymentBody
): Promise<Deployment> {
return this.post(`/api/deployment/${target_id}/copy`, body);
}
delete_deployment(id: string): Promise<Deployment> {
return this.delete(`/api/deployment/${id}/delete`);
}
update_deployment(deployment: Deployment): Promise<Deployment> {
return this.patch("/api/deployment/update", deployment);
}
rename_deployment(deployment_id: string, new_name: string) {
return this.patch(`/api/deployment/${deployment_id}/rename`, { new_name });
}
reclone_deployment(deployment_id: string): Promise<Update> {
return this.post(`/api/deployment/${deployment_id}/reclone`);
}
pull_deployment(deployment_id: string): Promise<Update> {
return this.post(`/api/deployment/${deployment_id}/pull`);
}
deploy_container(deployment_id: string): Promise<Update> {
return this.post(`/api/deployment/${deployment_id}/deploy`);
}
start_container(deployment_id: string): Promise<Update> {
return this.post(`/api/deployment/${deployment_id}/start_container`);
}
stop_container(deployment_id: string): Promise<Update> {
return this.post(`/api/deployment/${deployment_id}/stop_container`);
}
remove_container(deployment_id: string): Promise<Update> {
return this.post(`/api/deployment/${deployment_id}/remove_container`);
}
async download_container_log(
id: string,
name: string,
error?: boolean | undefined
) {
const log = await this.get_deployment_container_log(id, 5000);
const date = new Date();
fileDownload(
(error ? log.stderr : log.stdout) || "no log",
`${name}-${error ? "error-" : ""}log-${date
.toLocaleDateString()
.replaceAll("/", "-")}.txt`
);
}
// server
list_servers(query?: QueryObject): Promise<ServerWithStatus[]> {
return this.get("/api/server/list" + generateQuery(query));
}
get_server(server_id: string): Promise<ServerWithStatus> {
return this.get(`/api/server/${server_id}`);
}
get_server_action_state(id: string): Promise<ServerActionState> {
return this.get(`/api/server/${id}/action_state`);
}
get_server_github_accounts(id: string): Promise<string[]> {
// if (this.github_accounts_cache[id]) {
// return this.github_accounts_cache[id];
// } else {
// this.github_accounts_cache[id] = [];
// }
// this.github_accounts_cache[id] = await this.get(
// `/api/server/${id}/github_accounts`
// );
// return this.github_accounts_cache[id];
return this.get(`/api/server/${id}/github_accounts`);
}
get_server_docker_accounts(id: string): Promise<string[]> {
// if (this.docker_accounts_cache[id]) {
// return this.docker_accounts_cache[id];
// } else {
// this.docker_accounts_cache[id] = [];
// };
// this.docker_accounts_cache[id] = await this.get(
// `/api/server/${id}/docker_accounts`
// );
// return this.docker_accounts_cache[id];
return this.get(`/api/server/${id}/docker_accounts`);
}
get_server_available_secrets(id: string): Promise<string[]> {
// if (this.secrets_cache[id]) {
// return this.secrets_cache[id];
// } else {
// this.secrets_cache[id] = [];
// };
// console.log("loading");
// this.secrets_cache[id] = await this.get(`/api/server/${id}/secrets`);
// return this.secrets_cache[id];
return this.get(`/api/server/${id}/secrets`);
}
get_server_version(id: string): Promise<string> {
// if (this.server_version_cache[id]) {
// return this.server_version_cache[id];
// } else {
// this.server_version_cache[id] = "loading...";
// };
// this.server_version_cache[id] = await this.get(`/api/server/${id}/version`);
// return this.server_version_cache[id];
return this.get(`/api/server/${id}/version`);
}
get_server_system_info(id: string): Promise<SystemInformation> {
return this.get(`/api/server/${id}/system_information`);
}
create_server(body: CreateServerBody): Promise<Server> {
return this.post("/api/server/create", body);
}
create_full_server(server: Server): Promise<Server> {
return this.post("/api/server/create_full", server);
}
delete_server(id: string): Promise<Server> {
return this.delete(`/api/server/${id}/delete`);
}
update_server(server: Server): Promise<Server> {
return this.patch("/api/server/update", server);
}
get_server_stats(
server_id: string,
query?: SystemStatsQuery
): Promise<SystemStats> {
return this.get(
`/api/server/${server_id}/stats${generateQuery(query as any)}`
);
}
get_server_stats_history(
server_id: string,
query?: HistoricalStatsQuery
): Promise<SystemStatsRecord[]> {
return this.get(
`/api/server/${server_id}/stats/history${generateQuery(query as any)}`
);
}
get_server_stats_at_ts(
server_id: string,
ts: number
): Promise<SystemStatsRecord> {
return this.get(`/api/server/${server_id}/stats/at_ts?ts=${ts}`);
}
get_docker_networks(server_id: string): Promise<any[]> {
return this.get(`/api/server/${server_id}/networks`);
}
prune_docker_networks(server_id: string): Promise<Log> {
return this.post(`/api/server/${server_id}/networks/prune`);
}
get_docker_images(server_id: string): Promise<any[]> {
return this.get(`/api/server/${server_id}/images`);
}
prune_docker_images(server_id: string): Promise<Log> {
return this.post(`/api/server/${server_id}/images/prune`);
}
get_docker_containers(server_id: string): Promise<BasicContainerInfo[]> {
return this.get(`/api/server/${server_id}/containers`);
}
prune_docker_containers(server_id: string): Promise<Log> {
return this.post(`/api/server/${server_id}/containers/prune`);
}
// build
list_builds(query?: QueryObject): Promise<Build[]> {
return this.get("/api/build/list" + generateQuery(query));
}
get_build(build_id: string): Promise<Build> {
return this.get(`/api/build/${build_id}`);
}
get_build_action_state(id: string): Promise<BuildActionState> {
return this.get(`/api/build/${id}/action_state`);
}
get_build_versions(
id: string,
query?: BuildVersionsQuery
): Promise<BuildVersionsReponse[]> {
return this.get(`/api/build/${id}/versions${generateQuery(query as any)}`);
}
create_build(body: CreateBuildBody): Promise<Build> {
return this.post("/api/build/create", body);
}
create_full_build(build: Build): Promise<Build> {
return this.post("/api/build/create_full", build);
}
copy_build(target_id: string, body: CopyBuildBody): Promise<Build> {
return this.post(`/api/build/${target_id}/copy`, body);
}
delete_build(id: string): Promise<Build> {
return this.delete(`/api/build/${id}/delete`);
}
update_build(build: Build): Promise<Build> {
return this.patch("/api/build/update", build);
}
build(build_id: string): Promise<Update> {
return this.post(`/api/build/${build_id}/build`);
}
reclone_build(id: string): Promise<Update> {
return this.post(`/api/build/${id}/reclone`);
}
get_aws_builder_defaults(): Promise<AwsBuilderConfig> {
return this.get("/api/build/aws_builder_defaults");
}
get_docker_organizations(): Promise<string[]> {
return this.get("/api/build/docker_organizations");
}
// procedure
list_procedures(query?: QueryObject): Promise<Procedure[]> {
return this.get("/api/procedure/list" + generateQuery(query));
}
get_procedure(procedure_id: string): Promise<Procedure> {
return this.get(`/api/procedure/${procedure_id}`);
}
create_procedure(body: CreateProcedureBody): Promise<Procedure> {
return this.post("/api/procedure/create", body);
}
create_full_procedure(procedure: Procedure): Promise<Procedure> {
return this.post("/api/procedure/create_full", procedure);
}
delete_procedure(id: string): Promise<Procedure> {
return this.delete(`/api/procedure/${id}/delete`);
}
update_procedure(procedure: Procedure): Promise<Procedure> {
return this.patch("/api/procedure/update", procedure);
}
run_procedure(id: string): Promise<Update> {
return this.post(`/api/procedure/${id}/run`);
}
// group
list_groups(query?: QueryObject): Promise<Group[]> {
return this.get("/api/group/list" + generateQuery(query));
}
get_group(group_id: string): Promise<Group> {
return this.get(`/api/group/${group_id}`);
}
create_group(body: CreateGroupBody): Promise<Group> {
return this.post("/api/group/create", body);
}
create_full_group(group: Group): Promise<Group> {
return this.post("/api/group/create_full", group);
}
delete_group(id: string): Promise<Group> {
return this.delete(`/api/group/${id}/delete`);
}
update_group(group: Group): Promise<Group> {
return this.patch("/api/group/update", group);
}
// updates
// show_builds is only relevant for Deployment targets, must pass show_builds = true to include build updates of attached build_id
list_updates(
offset: number,
target?: UpdateTarget,
show_builds?: boolean,
operations?: Operation[]
): Promise<Update[]> {
return this.get(
`/api/update/list${generateQuery({
offset,
type: target && target.type,
id: target && target.id,
show_builds,
operations: operations?.join(","),
})}`
);
}
// api secrets
create_api_secret(body: CreateSecretBody): Promise<string> {
return this.post("/api/secret/create", body);
}
delete_api_secret(name: string): Promise<undefined> {
return this.delete(`/api/secret/delete/${name}`);
}
// permissions
update_user_permissions_on_target(
body: PermissionsUpdateBody
): Promise<Update> {
return this.post("/api/permissions/update", body);
}
modify_user_enabled(body: ModifyUserEnabledBody): Promise<Update> {
return this.post("/api/permissions/modify_enabled", body);
}
modify_user_create_server_permissions(
body: ModifyUserCreateServerBody
): Promise<Update> {
return this.post("/api/permissions/modify_create_server", body);
}
modify_user_create_build_permissions(
body: ModifyUserCreateBuildBody
): Promise<Update> {
return this.post("/api/permissions/modify_create_build", body);
}
get<R = any>(url: string): Promise<R> {
return axios({
method: "get",
url: this.baseURL + url,
headers: {
authorization: this.token ? `Bearer ${this.token}` : undefined,
},
}).then(({ data }) => data);
}
post<B = any, R = any>(url: string, body?: B): Promise<R> {
return axios({
method: "post",
url: this.baseURL + url,
headers: {
authorization: `Bearer ${this.token}`,
},
data: body,
}).then(({ data }) => data);
}
patch<B = any, R = any>(url: string, body: B): Promise<R> {
return axios({
method: "patch",
url: this.baseURL + url,
headers: {
authorization: `Bearer ${this.token}`,
},
data: body,
}).then(({ data }) => data);
}
delete<R = any>(url: string): Promise<R> {
return axios({
method: "delete",
url: this.baseURL + url,
headers: {
authorization: `Bearer ${this.token}`,
},
}).then(({ data }) => data);
}
}