dynamic github webhook base url

This commit is contained in:
beckerinj
2023-02-27 22:17:37 -05:00
parent bfb9d9e34d
commit 997e68a31d
13 changed files with 117 additions and 31 deletions

View File

@@ -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),
};

View File

@@ -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<String>,
// sent in auth header with req to periphery
pub passkey: String,

View File

@@ -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())

View File

@@ -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)

View File

@@ -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: () => <BuilderConfig />
},
user().admin && {
title: "collaborators",
element: () => <Owners />,
title: "permissions",
element: () => <Permissions />,
},
].filter((e) => e) as Tab[]
}

View File

@@ -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 (
<Show when={userCanUpdate()}>
<Grid class="config-item shadow">

View File

@@ -123,7 +123,7 @@ const DeploymentTabs: Component<{}> = () => {
},
],
user().admin && {
title: "collaborators",
title: "permissions",
element: () => <Permissions />,
},
]

View File

@@ -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 (
<Show when={deployment.loaded}>
<Grid class="config">

View File

@@ -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 (
<Grid class="config-item shadow">
<h1>webhook url</h1>
<Flex
justifyContent="space-between"
alignItems="center"
style={{ "flex-wrap": "wrap" }}
>
<Show when={listenerUrl()} fallback={<Loading type="three-dot" />}>
<div class="ellipsis" style={{ width: "250px" }}>
{listenerUrl()}
</div>
</Show>
<ConfirmButton
class="blue"
onFirstClick={() => {
copyToClipboard(listenerUrl() || "");
pushNotification("good", "copied url to clipboard");
}}
confirm={<Icon type="check" />}
>
<Icon type="clipboard" />
</ConfirmButton>
</Flex>
</Grid>
);
}
export default WebhookUrl;

View File

@@ -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();

View File

@@ -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<string> {
get_username(user_id: string): Promise<string> {
return this.get(`/api/username/${user_id}`);
}
async list_users(): Promise<User[]> {
list_users(): Promise<User[]> {
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<string> {
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<R = any>(url: string): Promise<R> {
return await axios({
get<R = any>(url: string): Promise<R> {
return axios({
method: "get",
url: this.baseURL + url,
headers: {
@@ -480,8 +484,8 @@ export class Client {
}).then(({ data }) => data);
}
async post<B = any, R = any>(url: string, body?: B): Promise<R> {
return await axios({
post<B = any, R = any>(url: string, body?: B): Promise<R> {
return axios({
method: "post",
url: this.baseURL + url,
headers: {
@@ -491,8 +495,8 @@ export class Client {
}).then(({ data }) => data);
}
async patch<B = any, R = any>(url: string, body: B): Promise<R> {
return await axios({
patch<B = any, R = any>(url: string, body: B): Promise<R> {
return axios({
method: "patch",
url: this.baseURL + url,
headers: {
@@ -502,8 +506,8 @@ export class Client {
}).then(({ data }) => data);
}
async delete<R = any>(url: string): Promise<R> {
return await axios({
delete<R = any>(url: string): Promise<R> {
return axios({
method: "delete",
url: this.baseURL + url,
headers: {

View File

@@ -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<User> {
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<String> {
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<Vec<User>> {
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<String> {
self.get("/api/github_webhook_base_url", Option::<()>::None)
.await
.context("failed at call to get_github_webhook_base_url")
}
async fn get<R: DeserializeOwned>(

View File

@@ -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<String>,
// sent in auth header with req to periphery
pub passkey: String,