mirror of
https://github.com/moghtech/komodo.git
synced 2026-04-29 12:43:26 -05:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6559814b1 | ||
|
|
c8c080183f | ||
|
|
597b67f799 | ||
|
|
ec52d5f422 | ||
|
|
34806304d6 | ||
|
|
87953d5495 | ||
|
|
b6c7c80c95 | ||
|
|
77e568d5c3 | ||
|
|
699fc51cf7 | ||
|
|
21029c90b7 | ||
|
|
6b0530eb7f | ||
|
|
f7061c7225 | ||
|
|
750f698369 | ||
|
|
ec5ef42298 | ||
|
|
46820b0044 | ||
|
|
425a6648f7 |
8
.vscode/tasks.json
vendored
8
.vscode/tasks.json
vendored
@@ -100,14 +100,6 @@
|
|||||||
"cwd": "${workspaceFolder}/lib/monitor_client"
|
"cwd": "${workspaceFolder}/lib/monitor_client"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "cargo",
|
|
||||||
"command": "publish",
|
|
||||||
"label": "publish monitor cli",
|
|
||||||
"options": {
|
|
||||||
"cwd": "${workspaceFolder}/cli"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "docker compose up -d",
|
"command": "docker compose up -d",
|
||||||
|
|||||||
34
Cargo.lock
generated
34
Cargo.lock
generated
@@ -740,7 +740,7 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core"
|
name = "core"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async_timing_util",
|
"async_timing_util",
|
||||||
@@ -759,7 +759,7 @@ dependencies = [
|
|||||||
"hmac",
|
"hmac",
|
||||||
"jwt",
|
"jwt",
|
||||||
"monitor_helpers",
|
"monitor_helpers",
|
||||||
"monitor_types 0.2.10",
|
"monitor_types 0.2.11",
|
||||||
"mungos",
|
"mungos",
|
||||||
"periphery_client",
|
"periphery_client",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -993,10 +993,10 @@ checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "db_client"
|
name = "db_client"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"monitor_types 0.2.10",
|
"monitor_types 0.2.11",
|
||||||
"mungos",
|
"mungos",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1857,12 +1857,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monitor_cli"
|
name = "monitor_cli"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async_timing_util",
|
"async_timing_util",
|
||||||
"clap",
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
"monitor_types 0.2.10",
|
"monitor_types 0.2.11",
|
||||||
"rand",
|
"rand",
|
||||||
"run_command",
|
"run_command",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -1874,12 +1874,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monitor_client"
|
name = "monitor_client"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"envy",
|
"envy",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"monitor_types 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"monitor_types 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
@@ -1891,11 +1891,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monitor_helpers"
|
name = "monitor_helpers"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
"monitor_types 0.2.10",
|
"monitor_types 0.2.11",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -1904,7 +1904,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monitor_periphery"
|
name = "monitor_periphery"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async_timing_util",
|
"async_timing_util",
|
||||||
@@ -1916,7 +1916,7 @@ dependencies = [
|
|||||||
"envy",
|
"envy",
|
||||||
"futures",
|
"futures",
|
||||||
"monitor_helpers",
|
"monitor_helpers",
|
||||||
"monitor_types 0.2.10",
|
"monitor_types 0.2.11",
|
||||||
"run_command",
|
"run_command",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
@@ -1930,7 +1930,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monitor_types"
|
name = "monitor_types"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bollard",
|
"bollard",
|
||||||
@@ -1947,9 +1947,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monitor_types"
|
name = "monitor_types"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7bf35db6341431dea9f062f5d676305a213834638410fb9cdc49ca2521635c43"
|
checksum = "b2b2809cdf9e2c1f1faa0093e6da57e6e4d5833f7dd492df490cc4c66f73a383"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bollard",
|
"bollard",
|
||||||
@@ -2196,11 +2196,11 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "periphery_client"
|
name = "periphery_client"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"monitor_types 0.2.10",
|
"monitor_types 0.2.11",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "monitor_cli"
|
name = "monitor_cli"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["MoghTech"]
|
authors = ["MoghTech"]
|
||||||
description = "monitor cli | tools to setup monitor system"
|
description = "monitor cli | tools to setup monitor system"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "core"
|
name = "core"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use crate::{
|
|||||||
state::{State, StateExtension},
|
state::{State, StateExtension},
|
||||||
};
|
};
|
||||||
|
|
||||||
const NUM_UPDATES_PER_PAGE: usize = 10;
|
const NUM_UPDATES_PER_PAGE: usize = 20;
|
||||||
|
|
||||||
pub fn router() -> Router {
|
pub fn router() -> Router {
|
||||||
Router::new().route(
|
Router::new().route(
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const Users = lazy(() => import("./components/users/Users"));
|
|||||||
const User = lazy(() => import("./components/users/User"));
|
const User = lazy(() => import("./components/users/User"));
|
||||||
const Stats = lazy(() => import("./components/stats/Stats"));
|
const Stats = lazy(() => import("./components/stats/Stats"));
|
||||||
const Account = lazy(() => import("./components/account/Account"));
|
const Account = lazy(() => import("./components/account/Account"));
|
||||||
|
const Updates = lazy(() => import("./components/Updates"));
|
||||||
|
|
||||||
const App: Component = () => {
|
const App: Component = () => {
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
@@ -19,6 +20,7 @@ const App: Component = () => {
|
|||||||
<Topbar />
|
<Topbar />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" component={Home} />
|
<Route path="/" component={Home} />
|
||||||
|
<Route path="/updates" component={Updates} />
|
||||||
<Route path="/build/:id" component={Build} />
|
<Route path="/build/:id" component={Build} />
|
||||||
<Route path="/deployment/:id" component={Deployment} />
|
<Route path="/deployment/:id" component={Deployment} />
|
||||||
<Route path="/server/:id" component={Server} />
|
<Route path="/server/:id" component={Server} />
|
||||||
|
|||||||
@@ -69,10 +69,10 @@ const CopyMenu: Component<{
|
|||||||
targetClass="blue"
|
targetClass="blue"
|
||||||
content={() => (
|
content={() => (
|
||||||
<Grid placeItems="center">
|
<Grid placeItems="center">
|
||||||
<Flex alignItems="center">
|
<Flex class="full-width" alignItems="center">
|
||||||
<Input
|
<Input
|
||||||
placeholder="copy name"
|
placeholder="copy name"
|
||||||
class="card dark"
|
class="card dark full-width"
|
||||||
style={{ padding: "0.5rem" }}
|
style={{ padding: "0.5rem" }}
|
||||||
value={newName()}
|
value={newName()}
|
||||||
onEdit={setNewName}
|
onEdit={setNewName}
|
||||||
@@ -87,6 +87,8 @@ const CopyMenu: Component<{
|
|||||||
targetClass="blue"
|
targetClass="blue"
|
||||||
targetStyle={{ display: "flex", gap: "0.5rem" }}
|
targetStyle={{ display: "flex", gap: "0.5rem" }}
|
||||||
searchStyle={{ width: "100%" }}
|
searchStyle={{ width: "100%" }}
|
||||||
|
menuClass="scroller"
|
||||||
|
menuStyle={{ "max-height": "40vh" }}
|
||||||
position="bottom right"
|
position="bottom right"
|
||||||
useSearch
|
useSearch
|
||||||
/>
|
/>
|
||||||
|
|||||||
157
frontend/src/components/Updates.tsx
Normal file
157
frontend/src/components/Updates.tsx
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
import { A } from "@solidjs/router";
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
createEffect,
|
||||||
|
createMemo,
|
||||||
|
createSignal,
|
||||||
|
For,
|
||||||
|
Show,
|
||||||
|
} from "solid-js";
|
||||||
|
import { OPERATIONS } from "..";
|
||||||
|
import { useAppDimensions } from "../state/DimensionProvider";
|
||||||
|
import { useAppState } from "../state/StateProvider";
|
||||||
|
import { Operation, Update as UpdateType, UpdateStatus } from "../types";
|
||||||
|
import { readableMonitorTimestamp, readableVersion } from "../util/helpers";
|
||||||
|
import Icon from "./shared/Icon";
|
||||||
|
import Input from "./shared/Input";
|
||||||
|
import Flex from "./shared/layout/Flex";
|
||||||
|
import Grid from "./shared/layout/Grid";
|
||||||
|
import Loading from "./shared/loading/Loading";
|
||||||
|
import Selector from "./shared/menu/Selector";
|
||||||
|
import UpdateMenu from "./update/UpdateMenu";
|
||||||
|
|
||||||
|
const Updates: Component<{}> = (p) => {
|
||||||
|
const { isMobile } = useAppDimensions();
|
||||||
|
const { updates, usernames, name_from_update_target } = useAppState();
|
||||||
|
const [operation, setOperation] = createSignal<Operation>();
|
||||||
|
createEffect(() => {
|
||||||
|
if (operation()) {
|
||||||
|
updates.load([operation()!]);
|
||||||
|
} else {
|
||||||
|
updates.load();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const [search, setSearch] = createSignal("");
|
||||||
|
const filtered_updates = createMemo(() => {
|
||||||
|
return updates.collection()?.filter((u) => {
|
||||||
|
const name = name_from_update_target(u.target);
|
||||||
|
if (name.includes(search())) return true;
|
||||||
|
const username = usernames.get(u.operator);
|
||||||
|
if (username?.includes(search())) return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Grid class="full-width card shadow">
|
||||||
|
<Flex alignItems="center" justifyContent="space-between">
|
||||||
|
<h1>updates</h1>
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Input class="lightgrey" placeholder="search" onEdit={setSearch} />
|
||||||
|
<Selector
|
||||||
|
label={isMobile() ? undefined : "operation: "}
|
||||||
|
selected={operation() ? operation()! : "all"}
|
||||||
|
items={["all", ...OPERATIONS]}
|
||||||
|
onSelect={(o) =>
|
||||||
|
o === "all"
|
||||||
|
? setOperation(undefined)
|
||||||
|
: setOperation(o.replaceAll(" ", "_") as Operation)
|
||||||
|
}
|
||||||
|
targetClass="blue"
|
||||||
|
position="bottom right"
|
||||||
|
searchStyle={{ width: "15rem" }}
|
||||||
|
menuClass="scroller"
|
||||||
|
menuStyle={{ "max-height": "50vh" }}
|
||||||
|
useSearch
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
<Show
|
||||||
|
when={updates.loaded()}
|
||||||
|
fallback={
|
||||||
|
<Flex justifyContent="center">
|
||||||
|
<Loading type="three-dot" />
|
||||||
|
</Flex>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<For each={filtered_updates()}>
|
||||||
|
{(update) => <Update update={update} />}
|
||||||
|
</For>
|
||||||
|
<Show when={!updates.noMore()}>
|
||||||
|
<button
|
||||||
|
class="grey full-width"
|
||||||
|
onClick={() =>
|
||||||
|
operation()
|
||||||
|
? updates.loadMore([operation()!])
|
||||||
|
: updates.loadMore()
|
||||||
|
}
|
||||||
|
>
|
||||||
|
load more
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
|
</Show>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Updates;
|
||||||
|
|
||||||
|
const Update: Component<{ update: UpdateType }> = (p) => {
|
||||||
|
const { isMobile } = useAppDimensions();
|
||||||
|
const { usernames, name_from_update_target } = useAppState();
|
||||||
|
const name = () => name_from_update_target(p.update.target);
|
||||||
|
const operation = () => {
|
||||||
|
if (p.update.operation === Operation.BuildBuild) {
|
||||||
|
return `build ${readableVersion(p.update.version!)}`;
|
||||||
|
}
|
||||||
|
return `${p.update.operation.replaceAll("_", " ")}${
|
||||||
|
p.update.version ? " " + readableVersion(p.update.version) : ""
|
||||||
|
}`;
|
||||||
|
};
|
||||||
|
const link_to = () => {
|
||||||
|
return p.update.target.type === "System"
|
||||||
|
? "/"
|
||||||
|
: `/${p.update.target.type.toLowerCase()}/${p.update.target.id}`;
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
class="card light shadow wrap"
|
||||||
|
justifyContent="space-between"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="space-between"
|
||||||
|
style={{ width: isMobile() ? "100%" : undefined }}
|
||||||
|
>
|
||||||
|
<A style={{ padding: 0 }} href={link_to()}>
|
||||||
|
<h2 class="text-hover">{name()}</h2>
|
||||||
|
</A>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
color: !p.update.success ? "rgb(182, 47, 52)" : "inherit",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{operation()}
|
||||||
|
</div>
|
||||||
|
<Show when={p.update.status === UpdateStatus.InProgress}>
|
||||||
|
<div style={{ opacity: 0.7 }}>(in progress)</div>
|
||||||
|
</Show>
|
||||||
|
</Flex>
|
||||||
|
<Flex
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="space-between"
|
||||||
|
style={{ width: isMobile() ? "100%" : undefined }}
|
||||||
|
>
|
||||||
|
<Flex gap="0.5rem">
|
||||||
|
<Icon type="user" />
|
||||||
|
<div>{usernames.get(p.update.operator)}</div>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<div style={{ "place-self": "center end" }}>
|
||||||
|
{readableMonitorTimestamp(p.update.start_ts)}
|
||||||
|
</div>
|
||||||
|
<UpdateMenu update={p.update} />
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -11,6 +11,8 @@ import BuildArgs from "./BuildArgs";
|
|||||||
import Version from "./Version";
|
import Version from "./Version";
|
||||||
import Repo from "./Repo";
|
import Repo from "./Repo";
|
||||||
import WebhookUrl from "./WebhookUrl";
|
import WebhookUrl from "./WebhookUrl";
|
||||||
|
import ExtraArgs from "./ExtraArgs";
|
||||||
|
import UseBuildx from "./UseBuildx";
|
||||||
|
|
||||||
const BuildConfig: Component<{}> = (p) => {
|
const BuildConfig: Component<{}> = (p) => {
|
||||||
const { build, reset, save, userCanUpdate } = useConfig();
|
const { build, reset, save, userCanUpdate } = useConfig();
|
||||||
@@ -23,6 +25,8 @@ const BuildConfig: Component<{}> = (p) => {
|
|||||||
<Docker />
|
<Docker />
|
||||||
<CliBuild />
|
<CliBuild />
|
||||||
<BuildArgs />
|
<BuildArgs />
|
||||||
|
<ExtraArgs />
|
||||||
|
<UseBuildx />
|
||||||
<Show when={userCanUpdate()}>
|
<Show when={userCanUpdate()}>
|
||||||
<WebhookUrl />
|
<WebhookUrl />
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
59
frontend/src/components/build/tabs/config/ExtraArgs.tsx
Normal file
59
frontend/src/components/build/tabs/config/ExtraArgs.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { Component, For, Show } from "solid-js";
|
||||||
|
import Icon from "../../../shared/Icon";
|
||||||
|
import Input from "../../../shared/Input";
|
||||||
|
import Flex from "../../../shared/layout/Flex";
|
||||||
|
import Grid from "../../../shared/layout/Grid";
|
||||||
|
import { useConfig } from "../Provider";
|
||||||
|
|
||||||
|
const ExtraArgs: Component<{}> = (p) => {
|
||||||
|
const { build, setBuild, userCanUpdate } = useConfig();
|
||||||
|
const onAdd = () => {
|
||||||
|
setBuild("docker_build_args", "extra_args", (extra_args: any) => [
|
||||||
|
...extra_args,
|
||||||
|
"",
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
const onRemove = (index: number) => {
|
||||||
|
setBuild("docker_build_args", "extra_args", (extra_args) =>
|
||||||
|
extra_args!.filter((_, i) => i !== index)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Grid class="config-item shadow">
|
||||||
|
<Flex justifyContent="space-between" alignItems="center">
|
||||||
|
<h1>extra args</h1>
|
||||||
|
<Show when={userCanUpdate()}>
|
||||||
|
<button class="green" onClick={onAdd}>
|
||||||
|
<Icon type="plus" />
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
|
</Flex>
|
||||||
|
<For each={[...build.docker_build_args!.extra_args!.keys()]}>
|
||||||
|
{(_, index) => (
|
||||||
|
<Flex
|
||||||
|
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||||
|
alignItems="center"
|
||||||
|
style={{ "flex-wrap": "wrap" }}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder="--extra-arg=value"
|
||||||
|
value={build.docker_build_args!.extra_args![index()]}
|
||||||
|
style={{ width: "80%" }}
|
||||||
|
onEdit={(value) =>
|
||||||
|
setBuild("docker_build_args", "extra_args", index(), value)
|
||||||
|
}
|
||||||
|
disabled={!userCanUpdate()}
|
||||||
|
/>
|
||||||
|
<Show when={userCanUpdate()}>
|
||||||
|
<button class="red" onClick={() => onRemove(index())}>
|
||||||
|
<Icon type="minus" />
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ExtraArgs;
|
||||||
30
frontend/src/components/build/tabs/config/UseBuildx.tsx
Normal file
30
frontend/src/components/build/tabs/config/UseBuildx.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { Component, Show } from "solid-js";
|
||||||
|
import Flex from "../../../shared/layout/Flex";
|
||||||
|
import { useConfig } from "../Provider";
|
||||||
|
|
||||||
|
const UseBuildx: Component<{}> = (p) => {
|
||||||
|
const { build, setBuild, userCanUpdate } = useConfig();
|
||||||
|
const use_buildx = () => build.docker_build_args?.use_buildx || false;
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
class="config-item shadow"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="space-between"
|
||||||
|
>
|
||||||
|
<h1>use buildx</h1>
|
||||||
|
<Show
|
||||||
|
when={userCanUpdate()}
|
||||||
|
fallback={<div>{use_buildx() ? "enabled" : "disabled"}</div>}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class={use_buildx() ? "green" : "red"}
|
||||||
|
onClick={() => setBuild("docker_build_args", "use_buildx", (c) => !c)}
|
||||||
|
>
|
||||||
|
{use_buildx() ? "enabled" : "disabled"}
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UseBuildx;
|
||||||
@@ -119,7 +119,18 @@ const Header: Component<{}> = (p) => {
|
|||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
<div style={{ opacity: 0.7 }}>{image()}</div>
|
<Show
|
||||||
|
when={deployment().deployment.build_id}
|
||||||
|
fallback={<div style={{ opacity: 0.7 }}>{image()}</div>}
|
||||||
|
>
|
||||||
|
<A
|
||||||
|
href={`/build/${deployment().deployment.build_id}`}
|
||||||
|
class="text-hover"
|
||||||
|
style={{ opacity: 0.7, padding: 0 }}
|
||||||
|
>
|
||||||
|
{image()}
|
||||||
|
</A>
|
||||||
|
</Show>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Show when={userCanUpdate()}>
|
<Show when={userCanUpdate()}>
|
||||||
<Flex alignItems="center">
|
<Flex alignItems="center">
|
||||||
|
|||||||
@@ -14,26 +14,29 @@ const Summary: Component<{}> = (p) => {
|
|||||||
const serverCount = useServerCount();
|
const serverCount = useServerCount();
|
||||||
return (
|
return (
|
||||||
<Grid
|
<Grid
|
||||||
class="card shadow"
|
class="full-size"
|
||||||
gridTemplateColumns={isMobile() ? "1fr" : "1fr 1fr"}
|
gridTemplateColumns={isMobile() ? "1fr" : "1fr 1fr"}
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
"box-sizing": "border-box",
|
|
||||||
}}
|
|
||||||
placeItems="center"
|
|
||||||
gap="0"
|
|
||||||
>
|
>
|
||||||
<div
|
<Grid class="card shadow full-size" placeItems="center">
|
||||||
style={{ width: `${PIE_CHART_SIZE}px`, height: `${PIE_CHART_SIZE}px` }}
|
<div
|
||||||
>
|
style={{
|
||||||
<PieChart title="deployments" sections={deployentCount()} />
|
width: `${PIE_CHART_SIZE}px`,
|
||||||
</div>
|
height: `${PIE_CHART_SIZE}px`,
|
||||||
<div
|
}}
|
||||||
style={{ width: `${PIE_CHART_SIZE}px`, height: `${PIE_CHART_SIZE}px` }}
|
>
|
||||||
>
|
<PieChart title="deployments" sections={deployentCount()} />
|
||||||
<PieChart title="servers" sections={serverCount()} />
|
</div>
|
||||||
</div>
|
</Grid>
|
||||||
|
<Grid class="card shadow full-size" placeItems="center">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: `${PIE_CHART_SIZE}px`,
|
||||||
|
height: `${PIE_CHART_SIZE}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PieChart title="servers" sections={serverCount()} />
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,166 +0,0 @@
|
|||||||
import { Component, createMemo, For, Show } from "solid-js";
|
|
||||||
import { useAppState } from "../../state/StateProvider";
|
|
||||||
import { DockerContainerState, ServerStatus } from "../../types";
|
|
||||||
import Grid from "../shared/layout/Grid";
|
|
||||||
import Flex from "../shared/layout/Flex";
|
|
||||||
|
|
||||||
const Summary: Component<{}> = (p) => {
|
|
||||||
return (
|
|
||||||
<Grid class="card shadow" gridTemplateRows="auto 1fr 1fr 1fr">
|
|
||||||
<h1>summary</h1>
|
|
||||||
<DeploymentsSummary />
|
|
||||||
<ServersSummary />
|
|
||||||
<BuildsSummary />
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Summary;
|
|
||||||
|
|
||||||
const SummaryItem: Component<{
|
|
||||||
title: string;
|
|
||||||
metrics: Array<{ title: string; class: string; count?: number }>;
|
|
||||||
}> = (p) => {
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
class="card light shadow wrap"
|
|
||||||
justifyContent="space-between"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<h2>{p.title}</h2>
|
|
||||||
<Flex class="wrap">
|
|
||||||
<For each={p.metrics}>
|
|
||||||
{(metric) => (
|
|
||||||
<Show when={metric?.count && metric.count > 0}>
|
|
||||||
<Flex gap="0.4rem" alignItems="center">
|
|
||||||
<div>{metric.title}</div>
|
|
||||||
<h2 class={metric.class}>{metric.count}</h2>
|
|
||||||
</Flex>
|
|
||||||
</Show>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const BuildsSummary = () => {
|
|
||||||
const { builds } = useAppState();
|
|
||||||
return (
|
|
||||||
<SummaryItem
|
|
||||||
title="builds"
|
|
||||||
metrics={[
|
|
||||||
{ title: "total", class: "text-green", count: builds.ids()?.length },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const DeploymentsSummary = () => {
|
|
||||||
const deployentCount = useDeploymentCount();
|
|
||||||
return (
|
|
||||||
<SummaryItem
|
|
||||||
title="deployments"
|
|
||||||
metrics={[
|
|
||||||
{
|
|
||||||
title: "total",
|
|
||||||
class: "text-green",
|
|
||||||
count: deployentCount().total,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "running",
|
|
||||||
class: "text-green",
|
|
||||||
count: deployentCount().running,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "stopped",
|
|
||||||
class: "text-red",
|
|
||||||
count: deployentCount().stopped,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "not deployed",
|
|
||||||
class: "text-blue",
|
|
||||||
count: deployentCount().notDeployed,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "unknown",
|
|
||||||
class: "text-blue",
|
|
||||||
count: deployentCount().unknown,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ServersSummary = () => {
|
|
||||||
const serverCount = useServerCount();
|
|
||||||
return (
|
|
||||||
<SummaryItem
|
|
||||||
title="servers"
|
|
||||||
metrics={[
|
|
||||||
{ title: "total", class: "text-green", count: serverCount().total },
|
|
||||||
{ title: "healthy", class: "text-green", count: serverCount().healthy },
|
|
||||||
{
|
|
||||||
title: "unhealthy",
|
|
||||||
class: "text-red",
|
|
||||||
count: serverCount().unhealthy,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "disabled",
|
|
||||||
class: "text-blue",
|
|
||||||
count: serverCount().disabled,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function useDeploymentCount() {
|
|
||||||
const { deployments } = useAppState();
|
|
||||||
const count = createMemo(() => {
|
|
||||||
const ids = deployments.ids();
|
|
||||||
if (!ids)
|
|
||||||
return { total: 0, running: 0, stopped: 0, notDeployed: 0, unknown: 0 };
|
|
||||||
let running = 0;
|
|
||||||
let stopped = 0;
|
|
||||||
let notDeployed = 0;
|
|
||||||
let unknown = 0;
|
|
||||||
for (const id of ids) {
|
|
||||||
const state = deployments.get(id)!.state;
|
|
||||||
if (state === DockerContainerState.NotDeployed) {
|
|
||||||
notDeployed++;
|
|
||||||
} else if (state === DockerContainerState.Running) {
|
|
||||||
running++;
|
|
||||||
} else if (state === DockerContainerState.Exited) {
|
|
||||||
stopped++;
|
|
||||||
} else if (state === DockerContainerState.Unknown) {
|
|
||||||
unknown++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { total: ids.length, running, stopped, notDeployed, unknown };
|
|
||||||
});
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
function useServerCount() {
|
|
||||||
const { servers } = useAppState();
|
|
||||||
const count = createMemo(() => {
|
|
||||||
const ids = servers.ids();
|
|
||||||
if (!ids) return { total: 0, healthy: 0, unhealthy: 0, disabled: 0 };
|
|
||||||
let healthy = 0;
|
|
||||||
let unhealthy = 0;
|
|
||||||
let disabled = 0;
|
|
||||||
for (const id of ids) {
|
|
||||||
const server = servers.get(id)!;
|
|
||||||
if (server.status === ServerStatus.Disabled) {
|
|
||||||
disabled++;
|
|
||||||
} else if (server.status === ServerStatus.Ok) {
|
|
||||||
healthy++;
|
|
||||||
} else if (server.status === ServerStatus.NotOk) {
|
|
||||||
unhealthy++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { total: ids.length, healthy, unhealthy, disabled };
|
|
||||||
});
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
@@ -14,18 +14,9 @@ import UpdateMenu from "../../update/UpdateMenu";
|
|||||||
import s from "./update.module.scss";
|
import s from "./update.module.scss";
|
||||||
|
|
||||||
const Update: Component<{ update: UpdateType }> = (p) => {
|
const Update: Component<{ update: UpdateType }> = (p) => {
|
||||||
const { deployments, servers, builds, usernames } = useAppState();
|
const { usernames, name_from_update_target } =
|
||||||
const name = () => {
|
useAppState();
|
||||||
if (p.update.target.type === "Deployment" && deployments.loaded()) {
|
const name = () => name_from_update_target(p.update.target);
|
||||||
return deployments.get(p.update.target.id!)?.deployment.name || "deleted";
|
|
||||||
} else if (p.update.target.type === "Server" && servers.loaded()) {
|
|
||||||
return servers.get(p.update.target.id)?.server.name || "deleted";
|
|
||||||
} else if (p.update.target.type === "Build" && builds.loaded()) {
|
|
||||||
return builds.get(p.update.target.id)?.name || "deleted";
|
|
||||||
} else {
|
|
||||||
return "monitor";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const operation = () => {
|
const operation = () => {
|
||||||
if (p.update.operation === Operation.BuildBuild) {
|
if (p.update.operation === Operation.BuildBuild) {
|
||||||
return `build ${readableVersion(p.update.version!)}`;
|
return `build ${readableVersion(p.update.version!)}`;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import { A } from "@solidjs/router";
|
||||||
import { Component, createEffect, createSignal, For, Show } from "solid-js";
|
import { Component, createEffect, createSignal, For, Show } from "solid-js";
|
||||||
|
import { OPERATIONS } from "../../..";
|
||||||
import { useAppState } from "../../../state/StateProvider";
|
import { useAppState } from "../../../state/StateProvider";
|
||||||
import { Operation } from "../../../types";
|
import { Operation } from "../../../types";
|
||||||
import Flex from "../../shared/layout/Flex";
|
import Flex from "../../shared/layout/Flex";
|
||||||
@@ -7,10 +9,6 @@ import Loading from "../../shared/loading/Loading";
|
|||||||
import Selector from "../../shared/menu/Selector";
|
import Selector from "../../shared/menu/Selector";
|
||||||
import Update from "./Update";
|
import Update from "./Update";
|
||||||
|
|
||||||
const OPERATIONS = Object.values(Operation)
|
|
||||||
.filter((e) => e !== "none" && !e.includes("user"))
|
|
||||||
.map((e) => e.replaceAll("_", " "));
|
|
||||||
|
|
||||||
const Updates: Component<{}> = () => {
|
const Updates: Component<{}> = () => {
|
||||||
const { updates } = useAppState();
|
const { updates } = useAppState();
|
||||||
const [operation, setOperation] = createSignal<Operation>();
|
const [operation, setOperation] = createSignal<Operation>();
|
||||||
@@ -24,8 +22,11 @@ const Updates: Component<{}> = () => {
|
|||||||
return (
|
return (
|
||||||
<Grid class="card shadow" style={{ "flex-grow": 1 }}>
|
<Grid class="card shadow" style={{ "flex-grow": 1 }}>
|
||||||
<Flex alignItems="center" justifyContent="space-between">
|
<Flex alignItems="center" justifyContent="space-between">
|
||||||
<h1>updates</h1>
|
<A href="/updates" style={{ padding: 0 }}>
|
||||||
|
<h1>updates</h1>
|
||||||
|
</A>
|
||||||
<Selector
|
<Selector
|
||||||
|
label="operation: "
|
||||||
selected={operation() ? operation()! : "all"}
|
selected={operation() ? operation()! : "all"}
|
||||||
items={["all", ...OPERATIONS]}
|
items={["all", ...OPERATIONS]}
|
||||||
onSelect={(o) =>
|
onSelect={(o) =>
|
||||||
@@ -50,7 +51,7 @@ const Updates: Component<{}> = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Grid class="updates-container-small scroller">
|
<Grid class="updates-container-small scroller">
|
||||||
<For each={updates.collection()!}>
|
<For each={updates.collection()}>
|
||||||
{(update) => <Update update={update} />}
|
{(update) => <Update update={update} />}
|
||||||
</For>
|
</For>
|
||||||
<Show when={!updates.noMore()}>
|
<Show when={!updates.noMore()}>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { readableStorageAmount } from "../../../util/helpers";
|
|||||||
import Flex from "../../shared/layout/Flex";
|
import Flex from "../../shared/layout/Flex";
|
||||||
import Grid from "../../shared/layout/Grid";
|
import Grid from "../../shared/layout/Grid";
|
||||||
import Loading from "../../shared/loading/Loading";
|
import Loading from "../../shared/loading/Loading";
|
||||||
import HoverMenu from "../../shared/menu/HoverMenu";
|
|
||||||
|
|
||||||
const Info: Component<{}> = (p) => {
|
const Info: Component<{}> = (p) => {
|
||||||
const { isMobile } = useAppDimensions();
|
const { isMobile } = useAppDimensions();
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ const Child: Component<{
|
|||||||
>
|
>
|
||||||
<Grid
|
<Grid
|
||||||
class={combineClasses(s.Menu, "shadow")}
|
class={combineClasses(s.Menu, "shadow")}
|
||||||
style={{ padding: (p.padding as any) || "1rem", ...p.style }}
|
style={{ padding: (p.padding as any) || "2rem", ...p.style }}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
onPointerDown={(e) => e.stopPropagation()}
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ const Selector: Component<{
|
|||||||
}> = (p) => {
|
}> = (p) => {
|
||||||
const [show, toggle] = useToggle();
|
const [show, toggle] = useToggle();
|
||||||
const [search, setSearch] = createSignal("");
|
const [search, setSearch] = createSignal("");
|
||||||
let ref: HTMLInputElement | undefined;
|
let search_ref: HTMLInputElement | undefined;
|
||||||
const current = () => (p.itemMap ? p.itemMap(p.selected) : p.selected);
|
const current = () => (p.itemMap ? p.itemMap(p.selected) : p.selected);
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (show()) setTimeout(() => ref?.focus(), 200);
|
if (show()) setTimeout(() => search_ref?.focus(), 200);
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Show
|
<Show
|
||||||
@@ -70,7 +70,7 @@ const Selector: Component<{
|
|||||||
<>
|
<>
|
||||||
<Show when={p.useSearch}>
|
<Show when={p.useSearch}>
|
||||||
<Input
|
<Input
|
||||||
ref={ref}
|
ref={search_ref}
|
||||||
placeholder="search"
|
placeholder="search"
|
||||||
value={search()}
|
value={search()}
|
||||||
onEdit={setSearch}
|
onEdit={setSearch}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
/* border: solid 1px rgba(2, 107, 121, 0.25); */
|
/* border: solid 1px rgba(2, 107, 121, 0.25); */
|
||||||
background-color: c.$grey;
|
background-color: c.$grey;
|
||||||
|
border: solid c.$darkgrey 2px;
|
||||||
z-index: 21;
|
z-index: 21;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -142,6 +143,11 @@ $anim-time: 350ms;
|
|||||||
background-color: rgba(0, 0, 0, 0.4);
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.CenterMenuHeader {
|
||||||
|
border-bottom: solid rgba(c.$lightgrey, 0.9) 2px;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.SelectorItem:hover {
|
.SelectorItem:hover {
|
||||||
background-color: c.$lightgrey;
|
background-color: c.$lightgrey;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
.TabTitle {
|
.TabTitle {
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
padding: 0.25rem 0.5rem;
|
padding: 0.75rem;
|
||||||
border-radius: 0rem;
|
border-radius: 0rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
import { Accessor, Component, For, ParentComponent, Show } from "solid-js";
|
import {
|
||||||
|
Accessor,
|
||||||
|
Component,
|
||||||
|
For,
|
||||||
|
JSXElement,
|
||||||
|
ParentComponent,
|
||||||
|
Show,
|
||||||
|
} from "solid-js";
|
||||||
import { COLORS } from "../../style/colors";
|
import { COLORS } from "../../style/colors";
|
||||||
import { SystemStats, SystemStatsRecord } from "../../types";
|
import { SystemStats, SystemStatsRecord } from "../../types";
|
||||||
import {
|
import {
|
||||||
convertTsMsToLocalUnixTsInSecs,
|
convertTsMsToLocalUnixTsInSecs,
|
||||||
get_to_one_sec_divisor,
|
get_to_one_sec_divisor,
|
||||||
} from "../../util/helpers";
|
} from "../../util/helpers";
|
||||||
|
import { useLocalStorage, useLocalStorageToggle } from "../../util/hooks";
|
||||||
|
import Flex from "../shared/layout/Flex";
|
||||||
import Grid from "../shared/layout/Grid";
|
import Grid from "../shared/layout/Grid";
|
||||||
import LightweightChart, { LightweightValue } from "../shared/LightweightChart";
|
import LightweightChart, { LightweightValue } from "../shared/LightweightChart";
|
||||||
import s from "./stats.module.scss";
|
import s from "./stats.module.scss";
|
||||||
@@ -15,13 +24,18 @@ const SMALL_CHART_HEIGHT = "150px";
|
|||||||
const SingleStatChart: Component<{
|
const SingleStatChart: Component<{
|
||||||
line?: LightweightValue[];
|
line?: LightweightValue[];
|
||||||
header: string;
|
header: string;
|
||||||
|
headerRight?: JSXElement;
|
||||||
label: string;
|
label: string;
|
||||||
color: string;
|
color: string;
|
||||||
small?: boolean;
|
small?: boolean;
|
||||||
disableScroll?: boolean;
|
disableScroll?: boolean;
|
||||||
}> = (p) => {
|
}> = (p) => {
|
||||||
return (
|
return (
|
||||||
<StatChartContainer header={p.header} small={p.small}>
|
<StatChartContainer
|
||||||
|
header={p.header}
|
||||||
|
headerRight={p.headerRight}
|
||||||
|
small={p.small}
|
||||||
|
>
|
||||||
<Show when={p.line}>
|
<Show when={p.line}>
|
||||||
<LightweightChart
|
<LightweightChart
|
||||||
class={s.LightweightChart}
|
class={s.LightweightChart}
|
||||||
@@ -44,23 +58,25 @@ const SingleStatChart: Component<{
|
|||||||
|
|
||||||
const StatChartContainer: ParentComponent<{
|
const StatChartContainer: ParentComponent<{
|
||||||
header: string;
|
header: string;
|
||||||
|
headerRight?: JSXElement;
|
||||||
small?: boolean;
|
small?: boolean;
|
||||||
}> = (p) => {
|
}> = (p) => {
|
||||||
return (
|
return (
|
||||||
<Grid
|
<Grid
|
||||||
gap="0.5rem"
|
gap="0.5rem"
|
||||||
class="card shadow"
|
class="card shadow full-width"
|
||||||
style={{
|
style={{
|
||||||
height: "fit-content",
|
height: "fit-content",
|
||||||
width: "100%",
|
|
||||||
"box-sizing": "border-box",
|
|
||||||
"padding-top": "0.5rem",
|
"padding-top": "0.5rem",
|
||||||
"padding-bottom": "0.2rem",
|
"padding-bottom": "0.2rem",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Show when={!p.small} fallback={<div>{p.header}</div>}>
|
<Flex justifyContent="space-between">
|
||||||
<h2>{p.header}</h2>
|
<Show when={!p.small} fallback={<div>{p.header}</div>}>
|
||||||
</Show>
|
<h2>{p.header}</h2>
|
||||||
|
</Show>
|
||||||
|
{p.headerRight}
|
||||||
|
</Flex>
|
||||||
{p.children}
|
{p.children}
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
@@ -152,20 +168,42 @@ export const MemChart: Component<{
|
|||||||
small?: boolean;
|
small?: boolean;
|
||||||
disableScroll?: boolean;
|
disableScroll?: boolean;
|
||||||
}> = (p) => {
|
}> = (p) => {
|
||||||
|
const [absolute, toggleAbsolute] = useLocalStorageToggle("stats-mem-mode-v2");
|
||||||
|
const symbol = () => (absolute() ? "GiB" : "%");
|
||||||
const line = () => {
|
const line = () => {
|
||||||
return p.stats()?.map((s) => {
|
if (absolute()) {
|
||||||
return {
|
return p.stats()?.map((s) => {
|
||||||
time: convertTsMsToLocalUnixTsInSecs(
|
return {
|
||||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
time: convertTsMsToLocalUnixTsInSecs(
|
||||||
),
|
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||||
value: (100 * s.mem_used_gb) / s.mem_total_gb,
|
),
|
||||||
};
|
value: s.mem_used_gb,
|
||||||
});
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return p.stats()?.map((s) => {
|
||||||
|
return {
|
||||||
|
time: convertTsMsToLocalUnixTsInSecs(
|
||||||
|
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||||
|
),
|
||||||
|
value: (100 * s.mem_used_gb) / s.mem_total_gb,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<SingleStatChart
|
<SingleStatChart
|
||||||
header="memory"
|
header="memory"
|
||||||
label="mem %"
|
headerRight={
|
||||||
|
<button
|
||||||
|
class="green"
|
||||||
|
style={{ padding: "0.2rem" }}
|
||||||
|
onClick={toggleAbsolute}
|
||||||
|
>
|
||||||
|
{symbol()}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
label={`mem ${symbol()}`}
|
||||||
color={COLORS.green}
|
color={COLORS.green}
|
||||||
line={line()}
|
line={line()}
|
||||||
small={p.small}
|
small={p.small}
|
||||||
@@ -179,20 +217,43 @@ export const DiskChart: Component<{
|
|||||||
small?: boolean;
|
small?: boolean;
|
||||||
disableScroll?: boolean;
|
disableScroll?: boolean;
|
||||||
}> = (p) => {
|
}> = (p) => {
|
||||||
|
const [absolute, toggleAbsolute] =
|
||||||
|
useLocalStorageToggle("stats-disk-mode-v2");
|
||||||
|
const symbol = () => (absolute() ? "GiB" : "%");
|
||||||
const line = () => {
|
const line = () => {
|
||||||
return p.stats()?.map((s) => {
|
if (absolute()) {
|
||||||
return {
|
return p.stats()?.map((s) => {
|
||||||
time: convertTsMsToLocalUnixTsInSecs(
|
return {
|
||||||
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
time: convertTsMsToLocalUnixTsInSecs(
|
||||||
),
|
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||||
value: (100 * s.disk.used_gb) / s.disk.total_gb,
|
),
|
||||||
};
|
value: s.disk.used_gb,
|
||||||
});
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return p.stats()?.map((s) => {
|
||||||
|
return {
|
||||||
|
time: convertTsMsToLocalUnixTsInSecs(
|
||||||
|
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
|
||||||
|
),
|
||||||
|
value: (100 * s.disk.used_gb) / s.disk.total_gb,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<SingleStatChart
|
<SingleStatChart
|
||||||
header="disk"
|
header="disk"
|
||||||
label="disk %"
|
headerRight={
|
||||||
|
<button
|
||||||
|
class="orange"
|
||||||
|
style={{ padding: "0.2rem" }}
|
||||||
|
onClick={toggleAbsolute}
|
||||||
|
>
|
||||||
|
{symbol()}
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
label={`disk ${symbol()}`}
|
||||||
color={COLORS.orange}
|
color={COLORS.orange}
|
||||||
line={line()}
|
line={line()}
|
||||||
small={p.small}
|
small={p.small}
|
||||||
|
|||||||
@@ -29,20 +29,23 @@ const HistoricalStats: Component<{
|
|||||||
const params = useParams();
|
const params = useParams();
|
||||||
const { timelength, page } = useStatsState();
|
const { timelength, page } = useStatsState();
|
||||||
const [stats, setStats] = createSignal<SystemStatsRecord[]>();
|
const [stats, setStats] = createSignal<SystemStatsRecord[]>();
|
||||||
createEffect(() => {
|
const [loading, setLoading] = createSignal(false);
|
||||||
client
|
createEffect(async () => {
|
||||||
|
setLoading(true);
|
||||||
|
const stats = await client
|
||||||
.get_server_stats_history(params.id, {
|
.get_server_stats_history(params.id, {
|
||||||
interval: timelength(),
|
interval: timelength(),
|
||||||
page: page(),
|
page: page(),
|
||||||
limit: 500,
|
limit: 500,
|
||||||
networks: true,
|
networks: true,
|
||||||
components: true,
|
components: true,
|
||||||
})
|
});
|
||||||
.then(setStats);
|
setStats(stats);
|
||||||
|
setLoading(false);
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Grid class={s.Content} placeItems="start center">
|
<Grid class={s.Content} placeItems="start center">
|
||||||
<Show when={stats()} fallback={<Loading type="three-dot" />}>
|
<Show when={stats() && !loading()} fallback={<Loading type="three-dot" />}>
|
||||||
<SimpleTabs
|
<SimpleTabs
|
||||||
localStorageKey="historical-stats-view-v3"
|
localStorageKey="historical-stats-view-v3"
|
||||||
defaultSelected="basic"
|
defaultSelected="basic"
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
import { useParams } from "@solidjs/router";
|
import { useParams } from "@solidjs/router";
|
||||||
import { ParentComponent, createContext, useContext, createSignal, createResource } from "solid-js";
|
import { ParentComponent, createContext, useContext, createSignal, createResource } from "solid-js";
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
|
import { useAppState } from "../../state/StateProvider";
|
||||||
import { SystemInformation, Timelength } from "../../types";
|
import { SystemInformation, Timelength } from "../../types";
|
||||||
import { useLocalStorage } from "../../util/hooks";
|
import { useLocalStorage } from "../../util/hooks";
|
||||||
|
|
||||||
|
export enum StatsView {
|
||||||
|
Current = "current",
|
||||||
|
Historical = "historical",
|
||||||
|
Info = "info"
|
||||||
|
}
|
||||||
|
|
||||||
const value = () => {
|
const value = () => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const [view, setView] = useLocalStorage("current", "stats-view-v1");
|
const [view, setView] = useLocalStorage(StatsView.Current, "stats-view-v2");
|
||||||
const [timelength, setTimelength] = useLocalStorage(
|
const [timelength, setTimelength] = useLocalStorage(
|
||||||
Timelength.OneMinute,
|
Timelength.OneMinute,
|
||||||
"stats-timelength-v3"
|
"stats-timelength-v3"
|
||||||
@@ -16,12 +23,7 @@ const value = () => {
|
|||||||
`${params.id}-stats-poll-v3`
|
`${params.id}-stats-poll-v3`
|
||||||
);
|
);
|
||||||
const [page, setPage] = createSignal(0);
|
const [page, setPage] = createSignal(0);
|
||||||
// const [wsOpen, setWsOpen] = createSignal(false);
|
|
||||||
const [sysInfo] = createResource<SystemInformation>(() =>
|
|
||||||
client.get_server_system_info(params.id)
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
sysInfo,
|
|
||||||
view,
|
view,
|
||||||
setView,
|
setView,
|
||||||
timelength,
|
timelength,
|
||||||
|
|||||||
@@ -1,20 +1,16 @@
|
|||||||
import { A, useParams } from "@solidjs/router";
|
import { A, useParams } from "@solidjs/router";
|
||||||
import {
|
import { Component, createResource, For, Match, Show, Switch } from "solid-js";
|
||||||
Component,
|
import { client } from "../..";
|
||||||
Match,
|
|
||||||
Show,
|
|
||||||
Switch,
|
|
||||||
} from "solid-js";
|
|
||||||
import { MAX_PAGE_WIDTH } from "../..";
|
|
||||||
import { useAppState } from "../../state/StateProvider";
|
import { useAppState } from "../../state/StateProvider";
|
||||||
import { ServerStatus, Timelength } from "../../types";
|
import { ServerStatus, Timelength } from "../../types";
|
||||||
|
import { readableStorageAmount } from "../../util/helpers";
|
||||||
import Icon from "../shared/Icon";
|
import Icon from "../shared/Icon";
|
||||||
import Flex from "../shared/layout/Flex";
|
import Flex from "../shared/layout/Flex";
|
||||||
import Grid from "../shared/layout/Grid";
|
import Grid from "../shared/layout/Grid";
|
||||||
import Selector from "../shared/menu/Selector";
|
import Selector from "../shared/menu/Selector";
|
||||||
import CurrentStats from "./CurrentStats";
|
import CurrentStats from "./CurrentStats";
|
||||||
import HistoricalStats from "./HistoricalStats";
|
import HistoricalStats from "./HistoricalStats";
|
||||||
import { StatsProvider, useStatsState } from "./Provider";
|
import { StatsProvider, useStatsState, StatsView } from "./Provider";
|
||||||
|
|
||||||
const TIMELENGTHS = [
|
const TIMELENGTHS = [
|
||||||
Timelength.FifteenSeconds,
|
Timelength.FifteenSeconds,
|
||||||
@@ -38,115 +34,182 @@ const Stats = () => {
|
|||||||
const StatsComp: Component<{}> = () => {
|
const StatsComp: Component<{}> = () => {
|
||||||
const { view } = useStatsState();
|
const { view } = useStatsState();
|
||||||
return (
|
return (
|
||||||
<Grid
|
<Grid class="full-width">
|
||||||
style={{
|
<Header />
|
||||||
width: "100%",
|
<Show when={view() === StatsView.Historical}>
|
||||||
"box-sizing": "border-box",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex justifyContent="space-between" style={{ width: "100%" }}>
|
|
||||||
<Header />
|
|
||||||
<SysInfo />
|
|
||||||
</Flex>
|
|
||||||
<Show when={view() === "historical"}>
|
|
||||||
<Flex alignItems="center" style={{ "place-self": "center" }}>
|
<Flex alignItems="center" style={{ "place-self": "center" }}>
|
||||||
<PageManager />
|
<PageManager />
|
||||||
</Flex>
|
</Flex>
|
||||||
</Show>
|
</Show>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={view() === "current"}>
|
<Match when={view() === StatsView.Current}>
|
||||||
<CurrentStats />
|
<CurrentStats />
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={view() === "historical"}>
|
<Match when={view() === StatsView.Historical}>
|
||||||
<HistoricalStats />
|
<HistoricalStats />
|
||||||
</Match>
|
</Match>
|
||||||
|
<Match when={view() === StatsView.Info}>
|
||||||
|
<SysInfo />
|
||||||
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Header: Component<{}> = (p) => {
|
export const Header: Component<{}> = (p) => {
|
||||||
const { servers } = useAppState();
|
const { servers, serverInfo } = useAppState();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const server = () => servers.get(params.id);
|
const server = () => servers.get(params.id);
|
||||||
const { view, setView, timelength, setTimelength, setPage, pollRate, setPollRate } = useStatsState();
|
const {
|
||||||
|
view,
|
||||||
|
setView,
|
||||||
|
timelength,
|
||||||
|
setTimelength,
|
||||||
|
setPage,
|
||||||
|
pollRate,
|
||||||
|
setPollRate,
|
||||||
|
} = useStatsState();
|
||||||
|
const sysInfo = () => serverInfo.get(params.id);
|
||||||
return (
|
return (
|
||||||
<Flex alignItems="center" style={{ height: "fit-content" }}>
|
<Flex alignItems="center" justifyContent="space-between">
|
||||||
<h1>{server()?.server.name}</h1>
|
<Flex alignItems="center" style={{ height: "fit-content" }}>
|
||||||
<A
|
<h1>{server()?.server.name}</h1>
|
||||||
href={`/server/${params.id}`}
|
<A
|
||||||
class={
|
href={`/server/${params.id}`}
|
||||||
server()?.server.enabled
|
class={
|
||||||
? server()?.status === ServerStatus.Ok
|
server()?.server.enabled
|
||||||
? "green"
|
? server()?.status === ServerStatus.Ok
|
||||||
: "red"
|
? "green"
|
||||||
: "blue"
|
: "red"
|
||||||
}
|
: "blue"
|
||||||
style={{
|
}
|
||||||
"border-radius": ".35rem",
|
style={{
|
||||||
transition: "background-color 125ms ease-in-out",
|
"border-radius": ".35rem",
|
||||||
}}
|
transition: "background-color 125ms ease-in-out",
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{server()?.status.replaceAll("_", " ").toUpperCase()}
|
|
||||||
</A>
|
|
||||||
<Grid gap="0" gridTemplateColumns="repeat(2, 1fr)">
|
|
||||||
<button
|
|
||||||
class={view() === "current" ? "selected" : "grey"}
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onClick={() => setView("current")}
|
|
||||||
>
|
|
||||||
current
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class={view() === "historical" ? "selected" : "grey"}
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
onClick={() => setView("historical")}
|
|
||||||
>
|
|
||||||
historical
|
|
||||||
</button>
|
|
||||||
</Grid>
|
|
||||||
<Show when={view() === "historical"}>
|
|
||||||
<Selector
|
|
||||||
targetClass="grey"
|
|
||||||
selected={timelength()}
|
|
||||||
items={TIMELENGTHS}
|
|
||||||
onSelect={(selected) => {
|
|
||||||
setPage(0);
|
|
||||||
setTimelength(selected as Timelength);
|
|
||||||
}}
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{server()?.status.replaceAll("_", " ").toUpperCase()}
|
||||||
|
</A>
|
||||||
|
<Selector
|
||||||
|
targetClass="blue"
|
||||||
|
selected={view()}
|
||||||
|
items={Object.values(StatsView)}
|
||||||
|
onSelect={(v) => setView(v as StatsView)}
|
||||||
|
position="bottom right"
|
||||||
/>
|
/>
|
||||||
</Show>
|
<Show when={view() === "historical"}>
|
||||||
<Show when={view() === "current"}>
|
|
||||||
<Flex gap="0.5rem" alignItems="center">
|
|
||||||
<div>poll:</div>
|
|
||||||
<Selector
|
<Selector
|
||||||
targetClass="grey"
|
targetClass="grey"
|
||||||
|
selected={timelength()}
|
||||||
|
items={TIMELENGTHS}
|
||||||
|
itemMap={(t) => t.replaceAll("-", " ")}
|
||||||
|
itemClass="full-width"
|
||||||
|
onSelect={(selected) => {
|
||||||
|
setPage(0);
|
||||||
|
setTimelength(selected as Timelength);
|
||||||
|
}}
|
||||||
|
position="bottom right"
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
<Show when={view() === "current"}>
|
||||||
|
<Selector
|
||||||
|
targetClass="grey"
|
||||||
|
label="poll: "
|
||||||
selected={pollRate()}
|
selected={pollRate()}
|
||||||
items={[Timelength.OneSecond, Timelength.FiveSeconds]}
|
items={[Timelength.OneSecond, Timelength.FiveSeconds]}
|
||||||
onSelect={(selected) => {
|
onSelect={(selected) => {
|
||||||
setPollRate(selected as Timelength);
|
setPollRate(selected as Timelength);
|
||||||
}}
|
}}
|
||||||
|
position="bottom right"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Show>
|
||||||
</Show>
|
</Flex>
|
||||||
|
<Flex>
|
||||||
|
<div>{sysInfo()?.cpu_brand}</div>
|
||||||
|
<div>
|
||||||
|
{sysInfo()?.core_count} core
|
||||||
|
{sysInfo()?.core_count && sysInfo()?.core_count! > 1 ? "s" : ""}
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SysInfo = () => {
|
const SysInfo = () => {
|
||||||
const { sysInfo } = useStatsState();
|
const { serverInfo } = useAppState();
|
||||||
|
const params = useParams();
|
||||||
|
const sysInfo = () => serverInfo.get(params.id);
|
||||||
|
const [stats] = createResource(() =>
|
||||||
|
client.get_server_stats(params.id, { disks: true })
|
||||||
|
);
|
||||||
|
const os_cards = () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: "os",
|
||||||
|
info: sysInfo()?.os,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "kernel",
|
||||||
|
info: sysInfo()?.kernel,
|
||||||
|
},
|
||||||
|
].filter((i) => i.info) as Array<{ label: string; info: string }>;
|
||||||
|
};
|
||||||
|
const cpu_cards = () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: "cpu",
|
||||||
|
info: sysInfo()?.cpu_brand,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "core count",
|
||||||
|
info: `${sysInfo()?.core_count} cores`,
|
||||||
|
},
|
||||||
|
].filter((i) => i.info) as Array<{ label: string; info: string }>;
|
||||||
|
};
|
||||||
|
const stats_cards = () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: "mem",
|
||||||
|
info:
|
||||||
|
stats()?.mem_total_gb &&
|
||||||
|
readableStorageAmount(stats()?.mem_total_gb!),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "disk",
|
||||||
|
info:
|
||||||
|
stats()?.disk.total_gb &&
|
||||||
|
readableStorageAmount(stats()?.disk.total_gb!),
|
||||||
|
},
|
||||||
|
].filter((i) => i.info) as Array<{ label: string; info: string }>;
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Grid class="full-width" placeItems="center">
|
||||||
alignItems="center"
|
<Show when={sysInfo()?.host_name}>
|
||||||
style={{ "place-self": "center end", width: "fit-content" }}
|
<Grid class="card full-width" style={{ "max-width": "700px" }}>
|
||||||
>
|
<InfoCard info={{ label: "hostname", info: sysInfo()?.host_name! }} />
|
||||||
<div>{sysInfo()?.os}</div>
|
</Grid>
|
||||||
{/* <div>{sysInfo()?.kernel}</div> */}
|
</Show>
|
||||||
<div>{sysInfo()?.cpu_brand}</div>
|
<Grid class="card full-width" style={{ "max-width": "700px" }}>
|
||||||
<div>{sysInfo()?.core_count} cores</div>
|
<For each={os_cards()}>{(i) => <InfoCard info={i} />}</For>
|
||||||
|
</Grid>
|
||||||
|
<Grid class="card full-width" style={{ "max-width": "700px" }}>
|
||||||
|
<For each={cpu_cards()}>{(i) => <InfoCard info={i} />}</For>
|
||||||
|
</Grid>
|
||||||
|
<Grid class="card full-width" style={{ "max-width": "700px" }}>
|
||||||
|
<For each={stats_cards()}>{(i) => <InfoCard info={i} />}</For>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const InfoCard: Component<{ info: { label: string; info: string } }> = (p) => {
|
||||||
|
return (
|
||||||
|
<Flex class="full-width" justifyContent="space-between">
|
||||||
|
<h2>{p.info.label}</h2>
|
||||||
|
<div>{p.info.info}</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ export const Search: Component<{}> = (p) => {
|
|||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
class={s.SearchInput}
|
class="lightgrey"
|
||||||
|
style={{ width: "30rem" }}
|
||||||
placeholder="search"
|
placeholder="search"
|
||||||
value={search.value()}
|
value={search.value()}
|
||||||
onEdit={input.onEdit}
|
onEdit={input.onEdit}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { UserProvider } from "./state/UserProvider";
|
|||||||
import { Client } from "./util/client";
|
import { Client } from "./util/client";
|
||||||
import { Router } from "@solidjs/router";
|
import { Router } from "@solidjs/router";
|
||||||
import { AppStateProvider } from "./state/StateProvider";
|
import { AppStateProvider } from "./state/StateProvider";
|
||||||
|
import { Operation } from "./types";
|
||||||
|
|
||||||
export const TOPBAR_HEIGHT = 50;
|
export const TOPBAR_HEIGHT = 50;
|
||||||
export const MAX_PAGE_WIDTH = 1200;
|
export const MAX_PAGE_WIDTH = 1200;
|
||||||
@@ -29,6 +30,10 @@ const token =
|
|||||||
|
|
||||||
export const client = new Client(MONITOR_BASE_URL, token);
|
export const client = new Client(MONITOR_BASE_URL, token);
|
||||||
|
|
||||||
|
export const OPERATIONS = Object.values(Operation)
|
||||||
|
.filter((e) => e !== "none" && !e.includes("user"))
|
||||||
|
.map((e) => e.replaceAll("_", " "));
|
||||||
|
|
||||||
export const { Notifications, pushNotification } = makeNotifications();
|
export const { Notifications, pushNotification } = makeNotifications();
|
||||||
|
|
||||||
client.initialize().then(() => {
|
client.initialize().then(() => {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
} from "./hooks";
|
} from "./hooks";
|
||||||
import connectToWs from "./ws";
|
import connectToWs from "./ws";
|
||||||
import { useUser } from "./UserProvider";
|
import { useUser } from "./UserProvider";
|
||||||
import { AwsBuilderConfig, PermissionLevel } from "../types";
|
import { AwsBuilderConfig, PermissionLevel, UpdateTarget } from "../types";
|
||||||
import { client } from "..";
|
import { client } from "..";
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
@@ -42,6 +42,7 @@ export type State = {
|
|||||||
aws_builder_config: Resource<AwsBuilderConfig>;
|
aws_builder_config: Resource<AwsBuilderConfig>;
|
||||||
docker_organizations: Resource<string[]>;
|
docker_organizations: Resource<string[]>;
|
||||||
github_webhook_base_url: Resource<string>;
|
github_webhook_base_url: Resource<string>;
|
||||||
|
name_from_update_target: (target: UpdateTarget) => string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const context = createContext<
|
const context = createContext<
|
||||||
@@ -148,6 +149,17 @@ export const AppStateProvider: ParentComponent = (p) => {
|
|||||||
aws_builder_config,
|
aws_builder_config,
|
||||||
docker_organizations,
|
docker_organizations,
|
||||||
github_webhook_base_url,
|
github_webhook_base_url,
|
||||||
|
name_from_update_target: (target) => {
|
||||||
|
if (target.type === "Deployment" && deployments) {
|
||||||
|
return deployments.get(target.id!)?.deployment.name || "deleted";
|
||||||
|
} else if (target.type === "Server" && servers) {
|
||||||
|
return servers.get(target.id)?.server.name || "deleted";
|
||||||
|
} else if (target.type === "Build" && builds) {
|
||||||
|
return builds.get(target.id)?.name || "deleted";
|
||||||
|
} else {
|
||||||
|
return "admin";
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// createEffect(() => {
|
// createEffect(() => {
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ export function useUpdates(target?: UpdateTarget, show_builds?: boolean) {
|
|||||||
operations
|
operations
|
||||||
);
|
);
|
||||||
updates.addManyToEnd(newUpdates);
|
updates.addManyToEnd(newUpdates);
|
||||||
if (newUpdates.length !== 10) {
|
if (newUpdates.length !== 20) {
|
||||||
setNoMore(true);
|
setNoMore(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -318,6 +318,22 @@ svg {
|
|||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.full-size {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-height {
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
// .hoverable {
|
// .hoverable {
|
||||||
// transition: all 250ms ease-in-out;
|
// transition: all 250ms ease-in-out;
|
||||||
// }
|
// }
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ export interface DockerBuildArgs {
|
|||||||
build_path: string;
|
build_path: string;
|
||||||
dockerfile_path?: string;
|
dockerfile_path?: string;
|
||||||
build_args?: EnvironmentVar[];
|
build_args?: EnvironmentVar[];
|
||||||
|
extra_args?: string[];
|
||||||
|
use_buildx?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BuildVersionsReponse {
|
export interface BuildVersionsReponse {
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
|
Build,
|
||||||
|
Deployment,
|
||||||
|
DeploymentWithContainerState,
|
||||||
DockerContainerState,
|
DockerContainerState,
|
||||||
EnvironmentVar,
|
EnvironmentVar,
|
||||||
|
Server,
|
||||||
ServerStatus,
|
ServerStatus,
|
||||||
|
ServerWithStatus,
|
||||||
Timelength,
|
Timelength,
|
||||||
|
UpdateTarget,
|
||||||
User,
|
User,
|
||||||
Version,
|
Version,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
@@ -238,10 +244,10 @@ export function readableVersion(version: Version) {
|
|||||||
|
|
||||||
export function readableUserType(user: User) {
|
export function readableUserType(user: User) {
|
||||||
if (user.github_id) {
|
if (user.github_id) {
|
||||||
return "github"
|
return "github";
|
||||||
} else if (user.google_id) {
|
} else if (user.google_id) {
|
||||||
return "google"
|
return "google";
|
||||||
} else {
|
} else {
|
||||||
return "local"
|
return "local";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "db_client"
|
name = "db_client"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "monitor_helpers"
|
name = "monitor_helpers"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["MoghTech"]
|
authors = ["MoghTech"]
|
||||||
description = "helpers used as dependency for mogh tech monitor"
|
description = "helpers used as dependency for mogh tech monitor"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "monitor_client"
|
name = "monitor_client"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["MoghTech"]
|
authors = ["MoghTech"]
|
||||||
description = "a client to interact with the monitor system"
|
description = "a client to interact with the monitor system"
|
||||||
@@ -9,8 +9,7 @@ license = "GPL-3.0-or-later"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
monitor_types = "0.2.10"
|
monitor_types = "0.2.11"
|
||||||
# monitor_types = { path = "../types" }
|
|
||||||
reqwest = { version = "0.11", features = ["json"] }
|
reqwest = { version = "0.11", features = ["json"] }
|
||||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||||
tokio = { version = "1.25", features = ["full"] }
|
tokio = { version = "1.25", features = ["full"] }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "periphery_client"
|
name = "periphery_client"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "monitor_types"
|
name = "monitor_types"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["MoghTech"]
|
authors = ["MoghTech"]
|
||||||
description = "types for the mogh tech monitor"
|
description = "types for the mogh tech monitor"
|
||||||
|
|||||||
@@ -155,6 +155,12 @@ pub struct DockerBuildArgs {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
pub build_args: Vec<EnvironmentVar>,
|
pub build_args: Vec<EnvironmentVar>,
|
||||||
|
#[serde(default)]
|
||||||
|
#[builder(default)]
|
||||||
|
pub extra_args: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
#[builder(default)]
|
||||||
|
pub use_buildx: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "monitor_periphery"
|
name = "monitor_periphery"
|
||||||
version = "0.2.10"
|
version = "0.2.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["MoghTech"]
|
authors = ["MoghTech"]
|
||||||
description = "monitor periphery binary"
|
description = "monitor periphery binary"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use types::{Build, DockerBuildArgs, EnvironmentVar, Log, Version};
|
|||||||
|
|
||||||
use crate::helpers::run_monitor_command;
|
use crate::helpers::run_monitor_command;
|
||||||
|
|
||||||
use super::docker_login;
|
use super::{docker_login, parse_extra_args};
|
||||||
|
|
||||||
pub async fn prune_images() -> Log {
|
pub async fn prune_images() -> Log {
|
||||||
let command = format!("docker image prune -a -f");
|
let command = format!("docker image prune -a -f");
|
||||||
@@ -32,6 +32,8 @@ pub async fn build(
|
|||||||
build_path,
|
build_path,
|
||||||
dockerfile_path,
|
dockerfile_path,
|
||||||
build_args,
|
build_args,
|
||||||
|
extra_args,
|
||||||
|
use_buildx,
|
||||||
} = docker_build_args
|
} = docker_build_args
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(anyhow!("build missing docker build args"))?;
|
.ok_or(anyhow!("build missing docker build args"))?;
|
||||||
@@ -46,6 +48,12 @@ pub async fn build(
|
|||||||
None => "Dockerfile".to_owned(),
|
None => "Dockerfile".to_owned(),
|
||||||
};
|
};
|
||||||
let build_args = parse_build_args(build_args);
|
let build_args = parse_build_args(build_args);
|
||||||
|
let extra_args = parse_extra_args(extra_args);
|
||||||
|
let buildx = if *use_buildx {
|
||||||
|
" buildx"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
let image_name = get_image_name(&name, docker_account, docker_organization);
|
let image_name = get_image_name(&name, docker_account, docker_organization);
|
||||||
let image_tags = image_tags(&image_name, &version);
|
let image_tags = image_tags(&image_name, &version);
|
||||||
let docker_push = if using_account {
|
let docker_push = if using_account {
|
||||||
@@ -54,7 +62,7 @@ pub async fn build(
|
|||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
let command = format!(
|
let command = format!(
|
||||||
"cd {} && docker build {build_args}{image_tags} -f {dockerfile_path} .{docker_push}",
|
"cd {} && docker{buildx} build {build_args}{extra_args}{image_tags} -f {dockerfile_path} .{docker_push}",
|
||||||
build_dir.display()
|
build_dir.display()
|
||||||
);
|
);
|
||||||
if *skip_secret_interp {
|
if *skip_secret_interp {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use types::{
|
|||||||
Conversion, Deployment, DockerContainerStats, DockerRunArgs, EnvironmentVar, Log, RestartMode,
|
Conversion, Deployment, DockerContainerStats, DockerRunArgs, EnvironmentVar, Log, RestartMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::helpers::run_monitor_command;
|
use crate::helpers::{run_monitor_command, docker::parse_extra_args};
|
||||||
|
|
||||||
use super::docker_login;
|
use super::docker_login;
|
||||||
|
|
||||||
@@ -197,12 +197,3 @@ fn parse_post_image(post_image: &Option<String>) -> String {
|
|||||||
String::new()
|
String::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_extra_args(extra_args: &Vec<String>) -> String {
|
|
||||||
let args = extra_args.join(" ");
|
|
||||||
if args.len() > 0 {
|
|
||||||
format!(" {args}")
|
|
||||||
} else {
|
|
||||||
args
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -38,3 +38,12 @@ pub async fn docker_login(
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_extra_args(extra_args: &Vec<String>) -> String {
|
||||||
|
let args = extra_args.join(" ");
|
||||||
|
if args.len() > 0 {
|
||||||
|
format!(" {args}")
|
||||||
|
} else {
|
||||||
|
args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -108,8 +108,7 @@ pub async fn test_build(monitor: &MonitorClient) -> anyhow::Result<Update> {
|
|||||||
});
|
});
|
||||||
build.docker_build_args = Some(DockerBuildArgs {
|
build.docker_build_args = Some(DockerBuildArgs {
|
||||||
build_path: "periphery".to_string(),
|
build_path: "periphery".to_string(),
|
||||||
dockerfile_path: None,
|
..Default::default()
|
||||||
build_args: Vec::new(),
|
|
||||||
});
|
});
|
||||||
let build = monitor.update_build(build).await?;
|
let build = monitor.update_build(build).await?;
|
||||||
println!("updated build.");
|
println!("updated build.");
|
||||||
|
|||||||
Reference in New Issue
Block a user