mirror of
https://github.com/moghtech/komodo.git
synced 2026-04-29 16:59:54 -05:00
select build version for monitor build
This commit is contained in:
+82
-2
@@ -5,10 +5,15 @@ use axum::{
|
||||
Extension, Json, Router,
|
||||
};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{Deserialize, Document, Serialize};
|
||||
use types::{traits::Permissioned, Build, BuildActionState, PermissionLevel};
|
||||
use mungos::{doc, Deserialize, Document, FindOptions, Serialize};
|
||||
use types::{
|
||||
traits::Permissioned, Build, BuildActionState, BuildVersionsReponse, Operation,
|
||||
PermissionLevel, UpdateStatus,
|
||||
};
|
||||
use typeshare::typeshare;
|
||||
|
||||
const NUM_VERSIONS_PER_PAGE: u64 = 10;
|
||||
|
||||
use crate::{
|
||||
auth::{RequestUser, RequestUserExtension},
|
||||
response,
|
||||
@@ -36,6 +41,16 @@ struct CopyBuildBody {
|
||||
server_id: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct BuildVersionsQuery {
|
||||
#[serde(default)]
|
||||
page: u32,
|
||||
major: Option<i32>,
|
||||
minor: Option<i32>,
|
||||
patch: Option<i32>,
|
||||
}
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
@@ -197,6 +212,21 @@ pub fn router() -> Router {
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/versions",
|
||||
get(
|
||||
|Extension(state): StateExtension,
|
||||
Extension(user): RequestUserExtension,
|
||||
Path(BuildId { id }),
|
||||
Query(query): Query<BuildVersionsQuery>| async move {
|
||||
let versions = state
|
||||
.get_build_versions(&id, &user, query)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(versions))
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
impl State {
|
||||
@@ -240,4 +270,54 @@ impl State {
|
||||
.clone();
|
||||
Ok(action_state)
|
||||
}
|
||||
|
||||
pub async fn get_build_versions(
|
||||
&self,
|
||||
id: &str,
|
||||
user: &RequestUser,
|
||||
query: BuildVersionsQuery,
|
||||
) -> anyhow::Result<Vec<BuildVersionsReponse>> {
|
||||
self.get_build_check_permissions(&id, user, PermissionLevel::Read)
|
||||
.await?;
|
||||
let mut filter = doc! {
|
||||
"target": {
|
||||
"type": "Build",
|
||||
"id": id
|
||||
},
|
||||
"operation": Operation::BuildBuild.to_string(),
|
||||
"status": UpdateStatus::Complete.to_string(),
|
||||
"success": true
|
||||
};
|
||||
if let Some(major) = query.major {
|
||||
filter.insert("version.major", major);
|
||||
}
|
||||
if let Some(minor) = query.minor {
|
||||
filter.insert("version.minor", minor);
|
||||
}
|
||||
if let Some(patch) = query.patch {
|
||||
filter.insert("version.patch", patch);
|
||||
}
|
||||
let versions = self
|
||||
.db
|
||||
.updates
|
||||
.get_some(
|
||||
filter,
|
||||
FindOptions::builder()
|
||||
.sort(doc! { "_id": -1 })
|
||||
.limit(NUM_VERSIONS_PER_PAGE as i64)
|
||||
.skip(query.page as u64 * NUM_VERSIONS_PER_PAGE)
|
||||
.build(),
|
||||
)
|
||||
.await
|
||||
.context("failed to pull versions from mongo")?
|
||||
.into_iter()
|
||||
.map(|u| (u.version, u.start_ts))
|
||||
.filter(|(v, _)| v.is_some())
|
||||
.map(|(v, ts)| BuildVersionsReponse {
|
||||
version: v.unwrap(),
|
||||
ts,
|
||||
})
|
||||
.collect();
|
||||
Ok(versions)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
|
||||
<g id="chevron_right_1_">
|
||||
<g>
|
||||
<path fill-rule="evenodd" fill="#fceade" clip-rule="evenodd" d="M13.71,9.29l-6-6C7.53,3.11,7.28,3,7,3C6.45,3,6,3.45,6,4
|
||||
c0,0.28,0.11,0.53,0.29,0.71L11.59,10l-5.29,5.29C6.11,15.47,6,15.72,6,16c0,0.55,0.45,1,1,1c0.28,0,0.53-0.11,0.71-0.29l6-6
|
||||
C13.89,10.53,14,10.28,14,10C14,9.72,13.89,9.47,13.71,9.29z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 698 B |
@@ -9,7 +9,7 @@ import {
|
||||
useContext,
|
||||
} from "solid-js";
|
||||
import { createStore, SetStoreFunction } from "solid-js/store";
|
||||
import { client } from "../../../..";
|
||||
import { client, pushNotification } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { useUser } from "../../../../state/UserProvider";
|
||||
import { Deployment, Operation, PermissionLevel } from "../../../../types";
|
||||
@@ -95,7 +95,11 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
|
||||
const save = () => {
|
||||
setDeployment("updating", true);
|
||||
client.update_deployment(deployment);
|
||||
client.update_deployment(deployment).catch(e => {
|
||||
console.error(e);
|
||||
pushNotification("bad", "update deployment failed");
|
||||
setDeployment("updating", false);
|
||||
});
|
||||
};
|
||||
|
||||
let update_unsub = () => {};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Component, Show } from "solid-js";
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import { client } from "../../../../..";
|
||||
import { useAppState } from "../../../../../state/StateProvider";
|
||||
import { combineClasses } from "../../../../../util/helpers";
|
||||
import { BuildVersionsReponse } from "../../../../../types";
|
||||
import { combineClasses, string_to_version, version_to_string } from "../../../../../util/helpers";
|
||||
import Input from "../../../../shared/Input";
|
||||
import Flex from "../../../../shared/layout/Flex";
|
||||
import Selector from "../../../../shared/menu/Selector";
|
||||
@@ -9,6 +11,12 @@ import { useConfig } from "../Provider";
|
||||
const Image: Component<{}> = (p) => {
|
||||
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
||||
const { builds } = useAppState();
|
||||
const [versions, setVersions] = createSignal<BuildVersionsReponse[]>([]);
|
||||
createEffect(() => {
|
||||
if (deployment.build_id) {
|
||||
client.get_build_versions(deployment.build_id).then(setVersions);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Flex
|
||||
class={combineClasses("config-item shadow")}
|
||||
@@ -48,14 +56,39 @@ const Image: Component<{}> = (p) => {
|
||||
? undefined
|
||||
: builds.ids()!.find((id) => builds.get(id)?.name === build)
|
||||
);
|
||||
setDeployment(
|
||||
"docker_run_args", { image: undefined }
|
||||
);
|
||||
setDeployment("docker_run_args", { image: "" });
|
||||
}}
|
||||
position="bottom right"
|
||||
disabled={!userCanUpdate()}
|
||||
useSearch
|
||||
/>
|
||||
<Show when={deployment.build_id}>
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={
|
||||
deployment.build_version
|
||||
? `v${version_to_string(deployment.build_version)}`
|
||||
: "latest"
|
||||
}
|
||||
items={[
|
||||
"latest",
|
||||
...versions().map((v) => `v${version_to_string(v.version)}`),
|
||||
]}
|
||||
onSelect={(version) => {
|
||||
if (version === "latest") {
|
||||
setDeployment("build_version", undefined);
|
||||
} else {
|
||||
setDeployment(
|
||||
"build_version",
|
||||
string_to_version(version.replace("v", ""))
|
||||
);
|
||||
}
|
||||
}}
|
||||
position="bottom right"
|
||||
disabled={!userCanUpdate()}
|
||||
useSearch
|
||||
/>
|
||||
</Show>
|
||||
</Show>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -3,14 +3,17 @@ import {
|
||||
Accessor,
|
||||
Component,
|
||||
createEffect,
|
||||
createMemo,
|
||||
createSignal,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { client } from "../../../..";
|
||||
import { SystemStats, SystemStatsRecord, Timelength } from "../../../../types";
|
||||
import { convertTsMsToLocalUnixTsInSecs } from "../../../../util/helpers";
|
||||
import {
|
||||
convertTsMsToLocalUnixTsInSecs,
|
||||
get_to_one_sec_divisor,
|
||||
} from "../../../../util/helpers";
|
||||
import { useLocalStorage } from "../../../../util/hooks";
|
||||
import Icon from "../../../shared/Icon";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import LightweightChart from "../../../shared/LightweightChart";
|
||||
@@ -24,6 +27,7 @@ const TIMELENGTHS = [
|
||||
Timelength.FifteenMinutes,
|
||||
Timelength.OneHour,
|
||||
Timelength.SixHours,
|
||||
Timelength.TwelveHours,
|
||||
Timelength.OneDay,
|
||||
];
|
||||
|
||||
@@ -34,17 +38,30 @@ const Stats: Component<{}> = (p) => {
|
||||
"server-stats-timelength-v3"
|
||||
);
|
||||
const [currStats, setCurrStats] = createSignal<SystemStats>();
|
||||
const [loadingCurr, setLoadingCurr] = createSignal(false);
|
||||
const [stats, setStats] = createSignal<SystemStatsRecord[]>();
|
||||
const [page, setPage] = createSignal(0);
|
||||
const load_curr_stats = () => {
|
||||
setLoadingCurr(true);
|
||||
client.get_server_stats(params.id).then((stats) => {
|
||||
setCurrStats(stats);
|
||||
setLoadingCurr(false);
|
||||
});
|
||||
};
|
||||
createEffect(() => {
|
||||
client.get_server_stats(params.id).then(setCurrStats);
|
||||
client
|
||||
.get_server_stats_history(params.id, {
|
||||
interval: timelength(),
|
||||
page: page(),
|
||||
limit: 1000,
|
||||
networks: true,
|
||||
components: true,
|
||||
})
|
||||
.then(setStats);
|
||||
});
|
||||
createEffect(() => {
|
||||
load_curr_stats();
|
||||
})
|
||||
// createEffect(() => console.log(stats()))
|
||||
return (
|
||||
<Grid
|
||||
@@ -58,7 +75,7 @@ const Stats: Component<{}> = (p) => {
|
||||
<Flex
|
||||
style={{ width: "100%" }}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
// justifyContent="space-between"
|
||||
>
|
||||
<Flex class="card light shadow" alignItems="center">
|
||||
<Show when={currStats()} fallback={<Loading type="three-dot" />}>
|
||||
@@ -66,7 +83,8 @@ const Stats: Component<{}> = (p) => {
|
||||
cpu: <h2>{currStats()!.cpu_perc.toFixed(1)}%</h2>
|
||||
</Grid>
|
||||
<Grid gap="0" placeItems="start center">
|
||||
mem: {currStats()!.mem_total_gb.toFixed(1)} GB
|
||||
mem:
|
||||
<div>{currStats()!.mem_total_gb.toFixed(1)} GB</div>
|
||||
<h2>
|
||||
{(
|
||||
(100 * currStats()!.mem_used_gb) /
|
||||
@@ -76,7 +94,8 @@ const Stats: Component<{}> = (p) => {
|
||||
</h2>
|
||||
</Grid>
|
||||
<Grid gap="0" placeItems="start center">
|
||||
disk: {currStats()!.disk.total_gb.toFixed(1)} GB
|
||||
disk:
|
||||
<div>{currStats()!.disk.total_gb.toFixed(1)} GB</div>
|
||||
<h2>
|
||||
{(
|
||||
(100 * currStats()!.disk.used_gb) /
|
||||
@@ -85,8 +104,31 @@ const Stats: Component<{}> = (p) => {
|
||||
% full
|
||||
</h2>
|
||||
</Grid>
|
||||
<button class="blue" onClick={load_curr_stats}>
|
||||
<Show when={!loadingCurr()} fallback={<Loading />}>
|
||||
<Icon type="refresh" />
|
||||
</Show>
|
||||
</button>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex class="card light shadow" alignItems="center">
|
||||
<button class="darkgrey" onClick={() => {
|
||||
setPage(page => page + 1);
|
||||
}}>
|
||||
<Icon type="chevron-left" />
|
||||
</button>
|
||||
<button class="darkgrey" onClick={() => {
|
||||
setPage(page => page > 0 ? page - 1 : 0);
|
||||
}}>
|
||||
<Icon type="chevron-right" />
|
||||
</button>
|
||||
<button class="darkgrey" onClick={() => {
|
||||
setPage(0)
|
||||
}}>
|
||||
<Icon type="double-chevron-right" />
|
||||
</button>
|
||||
<div>page: {page() + 1}</div>
|
||||
</Flex>
|
||||
<Selector
|
||||
selected={timelength()}
|
||||
items={TIMELENGTHS}
|
||||
@@ -132,7 +174,7 @@ const CpuChart: Component<{
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
style={{ height: "200px" }}
|
||||
lines={() => [{ color: "#184e9f", line: line()! }]}
|
||||
lines={() => [{ title: "%", color: "#184e9f", line: line()! }]}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
@@ -168,7 +210,7 @@ const MemChart: Component<{
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
style={{ height: "200px" }}
|
||||
lines={() => [{ color: "#184e9f", line: line()! }]}
|
||||
lines={() => [{ title: selected(), color: "#184e9f", line: line()! }]}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
@@ -204,7 +246,7 @@ const DiskChart: Component<{
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
style={{ height: "200px" }}
|
||||
lines={() => [{ color: "#184e9f", line: line()! }]}
|
||||
lines={() => [{ title: selected(), color: "#184e9f", line: line()! }]}
|
||||
/>
|
||||
</Grid>
|
||||
</Show>
|
||||
@@ -218,7 +260,11 @@ const NetworkChart: Component<{
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(s.ts),
|
||||
value: s.networks.map((n) => n.recieved_kb).reduce((p, c) => p + c),
|
||||
value:
|
||||
s.networks?.length || 0 > 0
|
||||
? s.networks!.map((n) => n.recieved_kb).reduce((p, c) => p + c) /
|
||||
get_to_one_sec_divisor(s.polling_rate)!
|
||||
: 0,
|
||||
};
|
||||
});
|
||||
};
|
||||
@@ -226,7 +272,11 @@ const NetworkChart: Component<{
|
||||
return p.stats()?.map((s) => {
|
||||
return {
|
||||
time: convertTsMsToLocalUnixTsInSecs(s.ts),
|
||||
value: s.networks.map((n) => n.transmitted_kb).reduce((p, c) => p + c),
|
||||
value:
|
||||
s.networks?.length || 0 > 0
|
||||
? s.networks!.map((n) => n.transmitted_kb).reduce((p, c) => p + c) /
|
||||
get_to_one_sec_divisor(s.polling_rate)!
|
||||
: 0,
|
||||
};
|
||||
});
|
||||
};
|
||||
@@ -234,14 +284,14 @@ const NetworkChart: Component<{
|
||||
<Show when={recv_line()}>
|
||||
<Grid gap="0" class="card dark shadow" style={{ height: "fit-content" }}>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h2>network kb</h2>
|
||||
<h2>network kb/s</h2>
|
||||
</Flex>
|
||||
<LightweightChart
|
||||
class={s.LightweightChart}
|
||||
style={{ height: "200px" }}
|
||||
lines={() => [
|
||||
{ color: "#41764c", line: recv_line()! },
|
||||
{ color: "#952E23", line: trans_line()! },
|
||||
{ title: "recv", color: "#41764c", line: recv_line()! },
|
||||
{ title: "send", color: "#952E23", line: trans_line()! },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
@@ -14,6 +14,7 @@ export type IconType =
|
||||
| "star-empty"
|
||||
| "star"
|
||||
| "chevron-left"
|
||||
| "chevron-right"
|
||||
| "trash"
|
||||
| "info-sign"
|
||||
| "menu"
|
||||
|
||||
@@ -1,17 +1,35 @@
|
||||
import { ColorType, createChart, IChartApi, ISeriesApi } from "lightweight-charts";
|
||||
import { Component, createEffect, createSignal, JSX, onCleanup, onMount } from "solid-js";
|
||||
import {
|
||||
ColorType,
|
||||
createChart,
|
||||
IChartApi,
|
||||
ISeriesApi,
|
||||
} from "lightweight-charts";
|
||||
import {
|
||||
Component,
|
||||
createEffect,
|
||||
createSignal,
|
||||
JSX,
|
||||
onCleanup,
|
||||
onMount,
|
||||
} from "solid-js";
|
||||
|
||||
type LinesData = {
|
||||
color: string,
|
||||
line: LineDataPoint[]
|
||||
}
|
||||
title: string;
|
||||
color: string;
|
||||
priceLineVisible?: boolean;
|
||||
line: LineDataPoint[];
|
||||
};
|
||||
|
||||
type LineDataPoint = {
|
||||
time: number;
|
||||
value: number;
|
||||
value: number;
|
||||
};
|
||||
|
||||
const LightweightChart: Component<{ style?: JSX.CSSProperties, class?: string, lines?: () => LinesData[] }> = (p) => {
|
||||
const LightweightChart: Component<{
|
||||
style?: JSX.CSSProperties;
|
||||
class?: string;
|
||||
lines?: () => LinesData[];
|
||||
}> = (p) => {
|
||||
let el: HTMLDivElement;
|
||||
const [chart, setChart] = createSignal<IChartApi>();
|
||||
let lineSeries: ISeriesApi<"Line">[] = [];
|
||||
@@ -41,24 +59,34 @@ const LightweightChart: Component<{ style?: JSX.CSSProperties, class?: string, l
|
||||
chart()!.removeSeries(series);
|
||||
}
|
||||
const series = p.lines().map((line) => {
|
||||
const series = chart()!.addLineSeries({ color: line.color });
|
||||
const series = chart()!.addLineSeries({
|
||||
color: line.color,
|
||||
title: line.title,
|
||||
priceLineVisible: line.priceLineVisible || false
|
||||
});
|
||||
series.setData(line.line as any);
|
||||
return series;
|
||||
});
|
||||
lineSeries = series;
|
||||
}
|
||||
})
|
||||
});
|
||||
const handleResize = () => {
|
||||
if (el && chart()) {
|
||||
chart()!.applyOptions({ width: el.clientWidth });
|
||||
}
|
||||
};
|
||||
addEventListener("resize", handleResize);
|
||||
onCleanup(() => {
|
||||
chart()?.remove();
|
||||
removeEventListener("resize", handleResize);
|
||||
})
|
||||
return <div ref={el!} class={p.class} style={{ width: "100%", height: "100%", ...p.style }} />;
|
||||
addEventListener("resize", handleResize);
|
||||
onCleanup(() => {
|
||||
chart()?.remove();
|
||||
removeEventListener("resize", handleResize);
|
||||
});
|
||||
return (
|
||||
<div
|
||||
ref={el!}
|
||||
class={p.class}
|
||||
style={{ width: "100%", height: "100%", ...p.style }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default LightweightChart;
|
||||
|
||||
@@ -40,6 +40,11 @@ export interface DockerBuildArgs {
|
||||
build_args?: EnvironmentVar[];
|
||||
}
|
||||
|
||||
export interface BuildVersionsReponse {
|
||||
version: Version;
|
||||
ts: string;
|
||||
}
|
||||
|
||||
export interface Deployment {
|
||||
_id?: string;
|
||||
name: string;
|
||||
@@ -245,9 +250,9 @@ export interface SystemStatsRecord {
|
||||
mem_used_gb: number;
|
||||
mem_total_gb: number;
|
||||
disk: DiskUsage;
|
||||
networks: SystemNetwork[];
|
||||
components: SystemComponent[];
|
||||
processes: SystemProcess[];
|
||||
networks?: SystemNetwork[];
|
||||
components?: SystemComponent[];
|
||||
processes?: SystemProcess[];
|
||||
polling_rate: Timelength;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
BasicContainerInfo,
|
||||
Build,
|
||||
BuildActionState,
|
||||
BuildVersionsReponse,
|
||||
Deployment,
|
||||
DeploymentActionState,
|
||||
DeploymentWithContainerState,
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
UserCredentials,
|
||||
} from "../types";
|
||||
import {
|
||||
BuildVersionsQuery,
|
||||
CopyBuildBody,
|
||||
CopyDeploymentBody,
|
||||
CreateBuildBody,
|
||||
@@ -241,8 +243,13 @@ export class Client {
|
||||
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(
|
||||
server_id: string,
|
||||
query?: SystemStatsQuery
|
||||
): Promise<SystemStats> {
|
||||
return this.get(
|
||||
`/api/server/${server_id}/stats${generateQuery(query as any)}`
|
||||
);
|
||||
}
|
||||
|
||||
get_server_stats_history(
|
||||
@@ -298,6 +305,9 @@ export class Client {
|
||||
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)}`);
|
||||
}
|
||||
|
||||
create_build(body: CreateBuildBody): Promise<Build> {
|
||||
return this.post("/api/build/create", body);
|
||||
|
||||
@@ -14,6 +14,13 @@ export interface CopyBuildBody {
|
||||
server_id: string;
|
||||
}
|
||||
|
||||
export interface BuildVersionsQuery {
|
||||
page?: number;
|
||||
major?: number;
|
||||
minor?: number;
|
||||
patch?: number;
|
||||
}
|
||||
|
||||
export interface CreateDeploymentBody {
|
||||
name: string;
|
||||
server_id: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DockerContainerState, EnvironmentVar, ServerStatus } from "../types";
|
||||
import { DockerContainerState, EnvironmentVar, ServerStatus, Timelength, Version } from "../types";
|
||||
|
||||
export function combineClasses(...classes: (string | false | undefined)[]) {
|
||||
return classes.filter((c) => (c ? true : false)).join(" ");
|
||||
@@ -173,3 +173,29 @@ export function deploymentHeaderStateClass(
|
||||
return "exited";
|
||||
}
|
||||
}
|
||||
|
||||
export function version_to_string(version: Version) {
|
||||
return `${version.major}.${version.minor}.${version.patch}`
|
||||
}
|
||||
|
||||
export function string_to_version(version: string): Version {
|
||||
const [major, minor, patch] = version.split(".")
|
||||
return {
|
||||
major: Number(major),
|
||||
minor: Number(minor),
|
||||
patch: Number(patch),
|
||||
}
|
||||
}
|
||||
|
||||
export function get_to_one_sec_divisor(timelength: Timelength) {
|
||||
// returns what the timelength needs to be divided to convert to per second values
|
||||
if (timelength === Timelength.OneSecond) {
|
||||
return 1
|
||||
} else if (timelength === Timelength.FiveSeconds) {
|
||||
return 5
|
||||
} else if (timelength === Timelength.ThirtySeconds) {
|
||||
return 30
|
||||
} else if (timelength === Timelength.OneMinute) {
|
||||
return 60
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use anyhow::Context;
|
||||
use monitor_types::{Build, BuildActionState, Update};
|
||||
use monitor_types::{Build, BuildActionState, BuildVersionsReponse, Update};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::MonitorClient;
|
||||
@@ -24,6 +24,21 @@ impl MonitorClient {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_build_versions(
|
||||
&self,
|
||||
build_id: &str,
|
||||
page: u32,
|
||||
major: impl Into<Option<u32>>,
|
||||
minor: impl Into<Option<u32>>,
|
||||
patch: impl Into<Option<u32>>,
|
||||
) -> anyhow::Result<BuildVersionsReponse> {
|
||||
self.get(
|
||||
&format!("/api/build/{build_id}/versions"),
|
||||
json!({ "page": page, "major": major.into(), "minor": minor.into(), "patch": patch.into() }),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn create_build(&self, name: &str, server_id: &str) -> anyhow::Result<Build> {
|
||||
self.post(
|
||||
"/api/build/create",
|
||||
|
||||
@@ -50,19 +50,16 @@ pub struct Build {
|
||||
pub on_clone: Option<Command>,
|
||||
|
||||
// build related
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub pre_build: Option<Command>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub docker_build_args: Option<DockerBuildArgs>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub docker_account: Option<String>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
#[serde(default)]
|
||||
#[diff(attr(#[serde(skip)]))]
|
||||
#[builder(setter(skip))]
|
||||
pub last_built_at: String,
|
||||
@@ -115,3 +112,10 @@ pub struct DockerBuildArgs {
|
||||
#[serde(default)]
|
||||
pub build_args: Vec<EnvironmentVar>,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct BuildVersionsReponse {
|
||||
pub version: Version,
|
||||
pub ts: String,
|
||||
}
|
||||
|
||||
@@ -35,36 +35,28 @@ pub struct Deployment {
|
||||
#[diff(attr(#[serde(skip_serializing_if = "docker_run_args_diff_no_change")]))]
|
||||
pub docker_run_args: DockerRunArgs,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub build_id: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub build_version: Option<Version>,
|
||||
|
||||
// deployment repo related
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub repo: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub branch: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub github_account: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub on_clone: Option<Command>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub on_pull: Option<Command>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub repo_mount: Option<Conversion>,
|
||||
|
||||
|
||||
@@ -57,11 +57,9 @@ pub struct Server {
|
||||
#[diff(attr(#[serde(skip_serializing_if = "timelength_diff_no_change")]))]
|
||||
pub stats_interval: Timelength,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub region: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub instance_id: Option<String>,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user