diff --git a/cli/src/helpers.rs b/cli/src/helpers.rs index e7b0a1715..fb32e09e0 100644 --- a/cli/src/helpers.rs +++ b/cli/src/helpers.rs @@ -82,6 +82,7 @@ pub fn gen_core_config(sub_matches: &ArgMatches) { }, jwt_secret: generate_secret(40), github_webhook_secret: generate_secret(30), + github_webhook_base_url: None, passkey: generate_secret(30), }; diff --git a/cli/src/types.rs b/cli/src/types.rs index 31fe7bfde..28346e193 100644 --- a/cli/src/types.rs +++ b/cli/src/types.rs @@ -31,6 +31,9 @@ pub struct CoreConfig { // used to verify validity from github webhooks pub github_webhook_secret: String, + // used to form the frontend listener url, if None will use 'host'. + pub github_webhook_base_url: Option, + // sent in auth header with req to periphery pub passkey: String, diff --git a/core/src/api/mod.rs b/core/src/api/mod.rs index 30beda652..75f069f30 100644 --- a/core/src/api/mod.rs +++ b/core/src/api/mod.rs @@ -45,6 +45,17 @@ pub fn router() -> Router { .map_err(handle_anyhow_error) }), ) + .route( + "/github_webhook_base_url", + get(|state: StateExtension| async move { + state + .config + .github_webhook_base_url + .as_ref() + .unwrap_or(&state.config.host) + .to_string() + }), + ) .route("/users", get(get_users)) .nest("/build", build::router()) .nest("/deployment", deployment::router()) diff --git a/docs/deployments.md b/docs/deployments.md index db6c9b7aa..65a78f235 100644 --- a/docs/deployments.md +++ b/docs/deployments.md @@ -1,3 +1,11 @@ -## deploying applications +# deploying applications + +Monitor can deploy any docker images that it can access with the configured docker accounts. + +## configuring the image + + + +[next: permissions](https://github.com/mbecker20/monitor/blob/main/docs/permissions.md) [back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md) \ No newline at end of file diff --git a/frontend/src/components/build/tabs/Tabs.tsx b/frontend/src/components/build/tabs/Tabs.tsx index 773cf67f9..47f743bcc 100644 --- a/frontend/src/components/build/tabs/Tabs.tsx +++ b/frontend/src/components/build/tabs/Tabs.tsx @@ -6,7 +6,7 @@ import SimpleTabs from "../../shared/tabs/SimpleTabs"; import { Tab } from "../../shared/tabs/Tabs"; import BuilderConfig from "./builder/BuilderConfig"; import BuildConfig from "./config/BuildConfig"; -import Owners from "./Permissions"; +import Permissions from "./Permissions"; import { ConfigProvider } from "./Provider"; const BuildTabs: Component<{}> = (p) => { @@ -30,8 +30,8 @@ const BuildTabs: Component<{}> = (p) => { element: () => }, user().admin && { - title: "collaborators", - element: () => , + title: "permissions", + element: () => , }, ].filter((e) => e) as Tab[] } diff --git a/frontend/src/components/build/tabs/config/ListenerUrl.tsx b/frontend/src/components/build/tabs/config/ListenerUrl.tsx index cd3abe9b3..7bff564ce 100644 --- a/frontend/src/components/build/tabs/config/ListenerUrl.tsx +++ b/frontend/src/components/build/tabs/config/ListenerUrl.tsx @@ -1,5 +1,5 @@ import { Component, Show } from "solid-js"; -import { pushNotification, URL } from "../../../.."; +import { pushNotification, MONITOR_BASE_URL } from "../../../.."; import { copyToClipboard, getId } from "../../../../util/helpers"; import ConfirmButton from "../../../shared/ConfirmButton"; import Icon from "../../../shared/Icon"; @@ -9,7 +9,7 @@ import { useConfig } from "../Provider"; const ListenerUrl: Component<{}> = (p) => { const { build, userCanUpdate } = useConfig(); - const listenerUrl = () => `${URL}/api/listener/build/${getId(build)}`; + const listenerUrl = () => `${MONITOR_BASE_URL}/api/listener/build/${getId(build)}`; return ( diff --git a/frontend/src/components/deployment/tabs/Tabs.tsx b/frontend/src/components/deployment/tabs/Tabs.tsx index bcd9ab6e0..2a1d8d62d 100644 --- a/frontend/src/components/deployment/tabs/Tabs.tsx +++ b/frontend/src/components/deployment/tabs/Tabs.tsx @@ -123,7 +123,7 @@ const DeploymentTabs: Component<{}> = () => { }, ], user().admin && { - title: "collaborators", + title: "permissions", element: () => , }, ] diff --git a/frontend/src/components/deployment/tabs/config/Config.tsx b/frontend/src/components/deployment/tabs/config/Config.tsx index dbfd7f582..397ac0586 100644 --- a/frontend/src/components/deployment/tabs/config/Config.tsx +++ b/frontend/src/components/deployment/tabs/config/Config.tsx @@ -17,19 +17,16 @@ import { Tab } from "../../../shared/tabs/Tabs"; import RepoMount from "./mount-repo/RepoMount"; import { OnClone, OnPull } from "./mount-repo/OnGit"; import Loading from "../../../shared/loading/Loading"; -import Permissions from "../Permissions"; -import { pushNotification, URL } from "../../../.."; +import { pushNotification, MONITOR_BASE_URL } from "../../../.."; import { combineClasses, copyToClipboard, getId } from "../../../../util/helpers"; import { useAppDimensions } from "../../../../state/DimensionProvider"; -import { useUser } from "../../../../state/UserProvider"; import SimpleTabs from "../../../shared/tabs/SimpleTabs"; import ExtraArgs from "./container/ExtraArgs"; const Config: Component<{}> = () => { const { deployment, reset, save, userCanUpdate } = useConfig(); - const { user } = useUser(); const { isMobile } = useAppDimensions(); - const listenerUrl = () => `${URL}/api/listener/deployment/${getId(deployment)}`; + const listenerUrl = () => `${MONITOR_BASE_URL}/api/listener/deployment/${getId(deployment)}`; return ( diff --git a/frontend/src/components/deployment/tabs/config/container/WebhookUrl.tsx b/frontend/src/components/deployment/tabs/config/container/WebhookUrl.tsx new file mode 100644 index 000000000..08077fd50 --- /dev/null +++ b/frontend/src/components/deployment/tabs/config/container/WebhookUrl.tsx @@ -0,0 +1,47 @@ +import { Component, createResource, Show } from "solid-js"; +import { client, pushNotification } from "../../../../.."; +import { copyToClipboard, getId } from "../../../../../util/helpers"; +import ConfirmButton from "../../../../shared/ConfirmButton"; +import Icon from "../../../../shared/Icon"; +import Flex from "../../../../shared/layout/Flex"; +import Grid from "../../../../shared/layout/Grid"; +import Loading from "../../../../shared/loading/Loading"; +import { useConfig } from "../Provider"; + +const WebhookUrl: Component<{}> = (p) => { + const { deployment } = useConfig(); + const [github_base_url] = createResource(() => client.get_github_webhook_base_url()); + const listenerUrl = () => { + if (github_base_url()) { + return `${github_base_url()}/api/listener/deployment/${getId(deployment)}`; + } + } + return ( + +

webhook url

+ + }> +
+ {listenerUrl()} +
+
+ { + copyToClipboard(listenerUrl() || ""); + pushNotification("good", "copied url to clipboard"); + }} + confirm={} + > + + +
+
+ ); +} + +export default WebhookUrl; \ No newline at end of file diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index c30dff1c8..af00688ce 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -15,19 +15,19 @@ import { AppStateProvider } from "./state/StateProvider"; export const TOPBAR_HEIGHT = 50; export const MAX_PAGE_WIDTH = 1200; -export const URL = +export const MONITOR_BASE_URL = import.meta.env.MODE === "production" ? location.origin : (import.meta.env.VITE_MONITOR_HOST as string) || "http://localhost:9000"; -export const UPDATE_WS_URL = URL.replace("http", "ws") + "/ws/update"; +export const UPDATE_WS_URL = MONITOR_BASE_URL.replace("http", "ws") + "/ws/update"; const token = (import.meta.env.VITE_ACCESS_TOKEN as string) || localStorage.getItem("access_token") || null; -export const client = new Client(URL, token); +export const client = new Client(MONITOR_BASE_URL, token); export const { Notifications, pushNotification } = makeNotifications(); diff --git a/frontend/src/util/client.ts b/frontend/src/util/client.ts index ff80c267e..105a2bdc8 100644 --- a/frontend/src/util/client.ts +++ b/frontend/src/util/client.ts @@ -1,6 +1,6 @@ import axios from "axios"; import fileDownload from "js-file-download"; -import { URL } from ".."; +import { MONITOR_BASE_URL } from ".."; import { AwsBuilderConfig, BasicContainerInfo, @@ -56,7 +56,7 @@ export class Client { const params = new URLSearchParams(location.search); const exchange_token = params.get("token"); if (exchange_token) { - history.replaceState({}, "", URL); + history.replaceState({}, "", MONITOR_BASE_URL); try { const jwt = await this.exchange_for_jwt(exchange_token); this.token = jwt; @@ -72,11 +72,11 @@ export class Client { } login_with_github() { - location.replace(`${URL}/auth/github/login`); + location.replace(`${MONITOR_BASE_URL}/auth/github/login`); } login_with_google() { - location.replace(`${URL}/auth/google/login`); + location.replace(`${MONITOR_BASE_URL}/auth/google/login`); } async login(credentials: UserCredentials) { @@ -111,11 +111,11 @@ export class Client { } } - async get_username(user_id: string): Promise { + get_username(user_id: string): Promise { return this.get(`/api/username/${user_id}`); } - async list_users(): Promise { + list_users(): Promise { return this.get("/api/users"); } @@ -123,6 +123,10 @@ export class Client { return this.post("/auth/exchange", { token: exchange_token }); } + get_github_webhook_base_url(): Promise { + return this.get("/api/github_webhook_base_url") + } + // deployment list_deployments( @@ -470,8 +474,8 @@ export class Client { return this.post("/api/permissions/modify_create_build", body); } - async get(url: string): Promise { - return await axios({ + get(url: string): Promise { + return axios({ method: "get", url: this.baseURL + url, headers: { @@ -480,8 +484,8 @@ export class Client { }).then(({ data }) => data); } - async post(url: string, body?: B): Promise { - return await axios({ + post(url: string, body?: B): Promise { + return axios({ method: "post", url: this.baseURL + url, headers: { @@ -491,8 +495,8 @@ export class Client { }).then(({ data }) => data); } - async patch(url: string, body: B): Promise { - return await axios({ + patch(url: string, body: B): Promise { + return axios({ method: "patch", url: this.baseURL + url, headers: { @@ -502,8 +506,8 @@ export class Client { }).then(({ data }) => data); } - async delete(url: string): Promise { - return await axios({ + delete(url: string): Promise { + return axios({ method: "delete", url: this.baseURL + url, headers: { diff --git a/lib/monitor_client/src/lib.rs b/lib/monitor_client/src/lib.rs index f1b0c4fd6..04b2e6daf 100644 --- a/lib/monitor_client/src/lib.rs +++ b/lib/monitor_client/src/lib.rs @@ -119,19 +119,31 @@ impl MonitorClient { json!({ "username": username.into(), "password": password.into() }), ) .await + .context("failed at call to create_user") } pub async fn get_user(&self) -> anyhow::Result { - self.get("/api/user", Option::<()>::None).await + self.get("/api/user", Option::<()>::None) + .await + .context("failed at call to get_user") } pub async fn get_username(&self, user_id: &str) -> anyhow::Result { self.get_string(&format!("/api/username/{user_id}"), Option::<()>::None) .await + .context("failed at call to get_username") } pub async fn list_users(&self) -> anyhow::Result> { - self.get("/api/users", Option::<()>::None).await + self.get("/api/users", Option::<()>::None) + .await + .context("failed at call to list_users") + } + + pub async fn get_github_webhook_base_url(&self) -> anyhow::Result { + self.get("/api/github_webhook_base_url", Option::<()>::None) + .await + .context("failed at call to get_github_webhook_base_url") } async fn get( diff --git a/lib/types/src/config.rs b/lib/types/src/config.rs index 116ecf8ff..60adb959d 100644 --- a/lib/types/src/config.rs +++ b/lib/types/src/config.rs @@ -43,6 +43,9 @@ pub struct CoreConfig { // used to verify validity from github webhooks pub github_webhook_secret: String, + // used to form the frontend listener url, if None will use 'host'. + pub github_webhook_base_url: Option, + // sent in auth header with req to periphery pub passkey: String,