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 = {}; github_accounts_cache: Record = {}; docker_accounts_cache: Record = {}; server_version_cache: Record = {}; 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 { 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 { 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 { return this.get(`/api/username/${user_id}`); } // admin only list_users(): Promise { return this.get("/api/users"); } // admin only get_user_by_id(user_id: string): Promise { return this.get(`/api/user/${user_id}`); } exchange_for_jwt(exchange_token: string): Promise { return this.post("/auth/exchange", { token: exchange_token }); } get_github_webhook_base_url(): Promise { return this.get("/api/github_webhook_base_url"); } update_description(body: UpdateDescriptionBody): Promise { return this.post("/api/update_description", body); } get_monitor_title(): Promise { return this.get("/api/title"); } // deployment list_deployments( query?: QueryObject ): Promise { return this.get("/api/deployment/list" + generateQuery(query)); } get_deployment(id: string): Promise { return this.get(`/api/deployment/${id}`); } get_deployment_action_state(id: string): Promise { return this.get(`/api/deployment/${id}/action_state`); } get_deployment_container_log(id: string, tail?: number): Promise { return this.get(`/api/deployment/${id}/log${generateQuery({ tail })}`); } get_deployment_container_stats(id: string): Promise { return this.get(`/api/deployment/${id}/stats`); } get_deployment_deployed_version(id: string): Promise { return this.get(`/api/deployment/${id}/deployed_version`); } create_deployment(body: CreateDeploymentBody): Promise { return this.post("/api/deployment/create", body); } create_full_deployment(deployment: Deployment): Promise { return this.post("/api/deployment/create_full", deployment); } copy_deployment( target_id: string, body: CopyDeploymentBody ): Promise { return this.post(`/api/deployment/${target_id}/copy`, body); } delete_deployment(id: string): Promise { return this.delete(`/api/deployment/${id}/delete`); } update_deployment(deployment: Deployment): Promise { 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 { return this.post(`/api/deployment/${deployment_id}/reclone`); } pull_deployment(deployment_id: string): Promise { return this.post(`/api/deployment/${deployment_id}/pull`); } deploy_container(deployment_id: string): Promise { return this.post(`/api/deployment/${deployment_id}/deploy`); } start_container(deployment_id: string): Promise { return this.post(`/api/deployment/${deployment_id}/start_container`); } stop_container(deployment_id: string): Promise { return this.post(`/api/deployment/${deployment_id}/stop_container`); } remove_container(deployment_id: string): Promise { 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 { return this.get("/api/server/list" + generateQuery(query)); } get_server(server_id: string): Promise { return this.get(`/api/server/${server_id}`); } get_server_action_state(id: string): Promise { return this.get(`/api/server/${id}/action_state`); } get_server_github_accounts(id: string): Promise { // 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 { // 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 { // 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 { // 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 { return this.get(`/api/server/${id}/system_information`); } create_server(body: CreateServerBody): Promise { return this.post("/api/server/create", body); } create_full_server(server: Server): Promise { return this.post("/api/server/create_full", server); } delete_server(id: string): Promise { return this.delete(`/api/server/${id}/delete`); } update_server(server: Server): Promise { return this.patch("/api/server/update", server); } get_server_stats( server_id: string, query?: SystemStatsQuery ): Promise { return this.get( `/api/server/${server_id}/stats${generateQuery(query as any)}` ); } get_server_stats_history( server_id: string, query?: HistoricalStatsQuery ): Promise { return this.get( `/api/server/${server_id}/stats/history${generateQuery(query as any)}` ); } get_server_stats_at_ts( server_id: string, ts: number ): Promise { return this.get(`/api/server/${server_id}/stats/at_ts?ts=${ts}`); } get_docker_networks(server_id: string): Promise { return this.get(`/api/server/${server_id}/networks`); } prune_docker_networks(server_id: string): Promise { return this.post(`/api/server/${server_id}/networks/prune`); } get_docker_images(server_id: string): Promise { return this.get(`/api/server/${server_id}/images`); } prune_docker_images(server_id: string): Promise { return this.post(`/api/server/${server_id}/images/prune`); } get_docker_containers(server_id: string): Promise { return this.get(`/api/server/${server_id}/containers`); } prune_docker_containers(server_id: string): Promise { return this.post(`/api/server/${server_id}/containers/prune`); } // build list_builds(query?: QueryObject): Promise { return this.get("/api/build/list" + generateQuery(query)); } get_build(build_id: string): Promise { return this.get(`/api/build/${build_id}`); } get_build_action_state(id: string): Promise { return this.get(`/api/build/${id}/action_state`); } get_build_versions( id: string, query?: BuildVersionsQuery ): Promise { return this.get(`/api/build/${id}/versions${generateQuery(query as any)}`); } create_build(body: CreateBuildBody): Promise { return this.post("/api/build/create", body); } create_full_build(build: Build): Promise { return this.post("/api/build/create_full", build); } copy_build(target_id: string, body: CopyBuildBody): Promise { return this.post(`/api/build/${target_id}/copy`, body); } delete_build(id: string): Promise { return this.delete(`/api/build/${id}/delete`); } update_build(build: Build): Promise { return this.patch("/api/build/update", build); } build(build_id: string): Promise { return this.post(`/api/build/${build_id}/build`); } reclone_build(id: string): Promise { return this.post(`/api/build/${id}/reclone`); } get_aws_builder_defaults(): Promise { return this.get("/api/build/aws_builder_defaults"); } get_docker_organizations(): Promise { return this.get("/api/build/docker_organizations"); } // procedure list_procedures(query?: QueryObject): Promise { return this.get("/api/procedure/list" + generateQuery(query)); } get_procedure(procedure_id: string): Promise { return this.get(`/api/procedure/${procedure_id}`); } create_procedure(body: CreateProcedureBody): Promise { return this.post("/api/procedure/create", body); } create_full_procedure(procedure: Procedure): Promise { return this.post("/api/procedure/create_full", procedure); } delete_procedure(id: string): Promise { return this.delete(`/api/procedure/${id}/delete`); } update_procedure(procedure: Procedure): Promise { return this.patch("/api/procedure/update", procedure); } run_procedure(id: string): Promise { return this.post(`/api/procedure/${id}/run`); } // group list_groups(query?: QueryObject): Promise { return this.get("/api/group/list" + generateQuery(query)); } get_group(group_id: string): Promise { return this.get(`/api/group/${group_id}`); } create_group(body: CreateGroupBody): Promise { return this.post("/api/group/create", body); } create_full_group(group: Group): Promise { return this.post("/api/group/create_full", group); } delete_group(id: string): Promise { return this.delete(`/api/group/${id}/delete`); } update_group(group: Group): Promise { 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 { 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 { return this.post("/api/secret/create", body); } delete_api_secret(name: string): Promise { return this.delete(`/api/secret/delete/${name}`); } // permissions update_user_permissions_on_target( body: PermissionsUpdateBody ): Promise { return this.post("/api/permissions/update", body); } modify_user_enabled(body: ModifyUserEnabledBody): Promise { return this.post("/api/permissions/modify_enabled", body); } modify_user_create_server_permissions( body: ModifyUserCreateServerBody ): Promise { return this.post("/api/permissions/modify_create_server", body); } modify_user_create_build_permissions( body: ModifyUserCreateBuildBody ): Promise { return this.post("/api/permissions/modify_create_build", body); } get(url: string): Promise { return axios({ method: "get", url: this.baseURL + url, headers: { authorization: this.token ? `Bearer ${this.token}` : undefined, }, }).then(({ data }) => data); } post(url: string, body?: B): Promise { return axios({ method: "post", url: this.baseURL + url, headers: { authorization: `Bearer ${this.token}`, }, data: body, }).then(({ data }) => data); } patch(url: string, body: B): Promise { return axios({ method: "patch", url: this.baseURL + url, headers: { authorization: `Bearer ${this.token}`, }, data: body, }).then(({ data }) => data); } delete(url: string): Promise { return axios({ method: "delete", url: this.baseURL + url, headers: { authorization: `Bearer ${this.token}`, }, }).then(({ data }) => data); } }