mirror of
https://github.com/moghtech/komodo.git
synced 2026-04-28 19:59:46 -05:00
begin frontend refactor for ephemeral build support
This commit is contained in:
@@ -3,8 +3,8 @@ WORKDIR /builder
|
||||
|
||||
COPY ./core ./core
|
||||
|
||||
# COPY ./lib/types ./lib/types
|
||||
# COPY ./lib/helpers ./lib/helpers
|
||||
COPY ./lib/types ./lib/types
|
||||
COPY ./lib/helpers ./lib/helpers
|
||||
|
||||
COPY ./lib/db_client ./lib/db_client
|
||||
COPY ./lib/periphery_client ./lib/periphery_client
|
||||
|
||||
@@ -47,10 +47,10 @@ export const NewDeployment: Component<{ serverID: string }> = (p) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const NewBuild: Component<{ serverID: string }> = (p) => {
|
||||
export const NewBuild: Component<{}> = (p) => {
|
||||
const [showNew, toggleShowNew] = useToggle();
|
||||
const create = (name: string) => {
|
||||
client.create_build({ name, server_id: p.serverID });
|
||||
client.create_build({ name });
|
||||
};
|
||||
return (
|
||||
<Show
|
||||
|
||||
@@ -11,31 +11,29 @@ type State = {
|
||||
|
||||
const context = createContext<State>();
|
||||
|
||||
export const ActionStateProvider: ParentComponent<{}> = (p) => {
|
||||
export const ActionStateProvider: ParentComponent<{ build_id: string }> = (p) => {
|
||||
const { ws } = useAppState();
|
||||
const params = useParams();
|
||||
const [actions, setActions] = createStore<BuildActionState>({
|
||||
building: false,
|
||||
recloning: false,
|
||||
updating: false,
|
||||
});
|
||||
createEffect(() => {
|
||||
client.get_build_action_state(params.id).then(setActions);
|
||||
client.get_build_action_state(p.build_id).then(setActions);
|
||||
});
|
||||
onCleanup(
|
||||
ws.subscribe([Operation.BuildBuild], (update) => {
|
||||
if (update.target.id === params.id) {
|
||||
if (update.target.id === p.build_id) {
|
||||
setActions("building", update.status !== UpdateStatus.Complete);
|
||||
}
|
||||
})
|
||||
);
|
||||
onCleanup(
|
||||
ws.subscribe([Operation.RecloneBuild], (update) => {
|
||||
if (update.target.id === params.id) {
|
||||
setActions("recloning", update.status !== UpdateStatus.Complete);
|
||||
}
|
||||
})
|
||||
);
|
||||
// onCleanup(
|
||||
// ws.subscribe([Operation.RecloneBuild], (update) => {
|
||||
// if (update.target.id === params.id) {
|
||||
// setActions("recloning", update.status !== UpdateStatus.Complete);
|
||||
// }
|
||||
// })
|
||||
// );
|
||||
// onCleanup(
|
||||
// ws.subscribe([DELETE_BUILD], ({ complete, buildID }) => {
|
||||
// if (buildID === selected.id()) {
|
||||
|
||||
@@ -10,14 +10,14 @@ import { useActionStates } from "./ActionStateProvider";
|
||||
import { client } from "../..";
|
||||
import { combineClasses, getId } from "../../util/helpers";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { PermissionLevel, ServerStatus } from "../../types";
|
||||
import { PermissionLevel, ServerStatus, ServerWithStatus } from "../../types";
|
||||
|
||||
const Actions: Component<{}> = (p) => {
|
||||
const { user } = useUser();
|
||||
const params = useParams() as { id: string };
|
||||
const { builds, servers } = useAppState();
|
||||
const build = () => builds.get(params.id)!;
|
||||
const server = () => build() && servers.get(build()!.server_id);
|
||||
const server = () => (build() && build().server_id) ? servers.get(build()!.server_id!) : undefined;
|
||||
const actions = useActionStates();
|
||||
const userCanExecute = () =>
|
||||
user().admin ||
|
||||
@@ -48,7 +48,7 @@ const Actions: Component<{}> = (p) => {
|
||||
</ConfirmButton>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
{/* <Flex class={combineClasses("action shadow")}>
|
||||
reclone{" "}
|
||||
<Show
|
||||
when={!actions.recloning}
|
||||
@@ -67,7 +67,7 @@ const Actions: Component<{}> = (p) => {
|
||||
<Icon type="reset" />
|
||||
</ConfirmButton>
|
||||
</Show>
|
||||
</Flex>
|
||||
</Flex> */}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Show>
|
||||
|
||||
@@ -35,7 +35,7 @@ const Build: Component<{}> = (p) => {
|
||||
onCleanup(() => unsub);
|
||||
return (
|
||||
<Show when={build()} fallback={<NotFound type="build" />}>
|
||||
<ActionStateProvider>
|
||||
<ActionStateProvider build_id={params.id}>
|
||||
<Grid
|
||||
style={{
|
||||
width: "100%",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useAppState } from "../../state/StateProvider";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import SimpleTabs from "../shared/tabs/SimpleTabs";
|
||||
import Summary from "./Summary";
|
||||
import Builds from "./Tree/Builds";
|
||||
import Groups from "./Tree/Groups";
|
||||
import { TreeProvider } from "./Tree/Provider";
|
||||
import Servers from "./Tree/Servers";
|
||||
@@ -36,6 +37,10 @@ const Home: Component<{}> = (p) => {
|
||||
title: "servers",
|
||||
element: () => <Servers serverIDs={servers.ids()!} showAdd />,
|
||||
},
|
||||
{
|
||||
title: "builds",
|
||||
element: () => <Builds />
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</TreeProvider>
|
||||
|
||||
146
frontend/src/components/home/Tree/Builds.tsx
Normal file
146
frontend/src/components/home/Tree/Builds.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import { A } from "@solidjs/router";
|
||||
import { Component, createMemo, createSignal, For, Show } from "solid-js";
|
||||
import { client } from "../../..";
|
||||
import { useAppState } from "../../../state/StateProvider";
|
||||
import { useUser } from "../../../state/UserProvider";
|
||||
import { PermissionLevel } from "../../../types";
|
||||
import { getId, readableMonitorTimestamp } from "../../../util/helpers";
|
||||
import { ActionStateProvider, useActionStates } from "../../build/ActionStateProvider";
|
||||
import { NewBuild } from "../../New";
|
||||
import ConfirmButton from "../../shared/ConfirmButton";
|
||||
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 { TreeSortType, TREE_SORTS, useTreeState } from "./Provider";
|
||||
|
||||
const Builds: Component<{}> = (p) => {
|
||||
const { user } = useUser();
|
||||
const { builds } = useAppState();
|
||||
const { sort, setSort, build_sorter } = useTreeState();
|
||||
const [buildFilter, setBuildFilter] = createSignal("");
|
||||
const buildIDs = createMemo(() => {
|
||||
if (builds.loaded()) {
|
||||
const filters = buildFilter()
|
||||
.split(" ")
|
||||
.filter((term) => term.length > 0)
|
||||
.map((term) => term.toLowerCase());
|
||||
return builds
|
||||
.ids()!
|
||||
.filter((id) => {
|
||||
const name = builds.get(id)!.name;
|
||||
for (const term of filters) {
|
||||
if (!name.includes(term)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.sort(build_sorter());
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Grid>
|
||||
<Grid gridTemplateColumns="1fr auto auto">
|
||||
<Input
|
||||
placeholder="filter builds"
|
||||
value={buildFilter()}
|
||||
onEdit={setBuildFilter}
|
||||
style={{ width: "100%", padding: "0.5rem" }}
|
||||
/>
|
||||
<Selector
|
||||
selected={sort()}
|
||||
items={TREE_SORTS as any as string[]}
|
||||
onSelect={(mode) => setSort(mode as TreeSortType)}
|
||||
position="bottom right"
|
||||
targetClass="blue"
|
||||
targetStyle={{ height: "100%" }}
|
||||
containerStyle={{ height: "100%" }}
|
||||
/>
|
||||
<Show when={user().admin || user().create_build_permissions}>
|
||||
<NewBuild />
|
||||
</Show>
|
||||
</Grid>
|
||||
<For each={buildIDs()}>
|
||||
{(id) => (
|
||||
<ActionStateProvider build_id={id}>
|
||||
<Build id={id} />
|
||||
</ActionStateProvider>
|
||||
)}
|
||||
</For>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const Build: Component<{ id: string }> = (p) => {
|
||||
const { user } = useUser();
|
||||
const { builds } = useAppState();
|
||||
const build = () => builds.get(p.id)!
|
||||
const version = () => {
|
||||
return `v${build().version.major}.${build().version.minor}.${
|
||||
build().version.patch
|
||||
}`;
|
||||
};
|
||||
const lastBuiltAt = () => {
|
||||
if (
|
||||
build().last_built_at === undefined ||
|
||||
build().last_built_at?.length === 0 ||
|
||||
build().last_built_at === "never"
|
||||
) {
|
||||
return "not built";
|
||||
} else {
|
||||
return readableMonitorTimestamp(build().last_built_at!);
|
||||
}
|
||||
};
|
||||
const actions = useActionStates();
|
||||
const userCanExecute = () =>
|
||||
user().admin ||
|
||||
build().permissions![getId(user())] === PermissionLevel.Execute ||
|
||||
build().permissions![getId(user())] === PermissionLevel.Update;
|
||||
return (
|
||||
<A
|
||||
href={`/build/${p.id}`}
|
||||
class="card light shadow"
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "fit-content",
|
||||
"box-sizing": "border-box",
|
||||
"justify-content": "space-between",
|
||||
}}
|
||||
>
|
||||
<h1 style={{ "font-size": "1.25rem" }}>{build().name}</h1>
|
||||
<Flex alignItems="center">
|
||||
<div>{version()}</div>
|
||||
<div style={{ opacity: 0.7 }}>{lastBuiltAt()}</div>
|
||||
<Show when={userCanExecute()}>
|
||||
<Show
|
||||
when={!actions.building}
|
||||
fallback={
|
||||
<button class="green" onClick={e => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}>
|
||||
<Loading type="spinner" />
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
onConfirm={() => {
|
||||
client.build(p.id);
|
||||
}}
|
||||
>
|
||||
<Icon type="build" width="0.9rem" />
|
||||
</ConfirmButton>
|
||||
</Show>
|
||||
</Show>
|
||||
</Flex>
|
||||
</A>
|
||||
);
|
||||
};
|
||||
|
||||
export default Builds;
|
||||
@@ -6,7 +6,7 @@ export const TREE_SORTS = ["name", "created"] as const;
|
||||
export type TreeSortType = typeof TREE_SORTS[number];
|
||||
|
||||
const value = () => {
|
||||
const { servers, groups } = useAppState();
|
||||
const { servers, groups, builds } = useAppState();
|
||||
const [sort, setSort] = useLocalStorage<TreeSortType>(
|
||||
TREE_SORTS[0],
|
||||
"home-sort-v1"
|
||||
@@ -29,7 +29,7 @@ const value = () => {
|
||||
}
|
||||
};
|
||||
const group_sorter = () => {
|
||||
if (!groups.loaded) return () => 0;
|
||||
if (!groups.loaded()) return () => 0;
|
||||
if (sort() === "name") {
|
||||
return (a: string, b: string) => {
|
||||
const ga = groups.get(a)!;
|
||||
@@ -44,12 +44,30 @@ const value = () => {
|
||||
} else {
|
||||
return () => 0;
|
||||
}
|
||||
};
|
||||
const build_sorter = () => {
|
||||
if (!builds.loaded()) return () => 0;
|
||||
if (sort() === "name") {
|
||||
return (a: string, b: string) => {
|
||||
const ba = builds.get(a)!;
|
||||
const bb = builds.get(b)!;
|
||||
if (ba.name < bb.name) {
|
||||
return -1;
|
||||
} else if (ba.name > bb.name) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
} else {
|
||||
return () => 0;
|
||||
}
|
||||
};
|
||||
return {
|
||||
sort,
|
||||
setSort,
|
||||
server_sorter,
|
||||
group_sorter,
|
||||
build_sorter
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ const ConfirmButton: Component<{
|
||||
onBlur={() => set(false)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (confirm()) {
|
||||
p.onConfirm && p.onConfirm();
|
||||
} else {
|
||||
|
||||
@@ -21,12 +21,12 @@ export interface Build {
|
||||
_id?: string;
|
||||
name: string;
|
||||
permissions?: PermissionsMap;
|
||||
server_id: string;
|
||||
server_id?: string;
|
||||
aws_config?: AwsBuilderConfig;
|
||||
version: Version;
|
||||
repo?: string;
|
||||
branch?: string;
|
||||
github_account?: string;
|
||||
on_clone?: Command;
|
||||
pre_build?: Command;
|
||||
docker_build_args?: DockerBuildArgs;
|
||||
docker_account?: string;
|
||||
@@ -37,7 +37,6 @@ export interface Build {
|
||||
|
||||
export interface BuildActionState {
|
||||
building: boolean;
|
||||
recloning: boolean;
|
||||
updating: boolean;
|
||||
}
|
||||
|
||||
@@ -58,6 +57,17 @@ export interface BuildVersionsReponse {
|
||||
ts: string;
|
||||
}
|
||||
|
||||
export interface AwsBuilderConfig {
|
||||
region?: string;
|
||||
instance_type?: string;
|
||||
ami_id?: string;
|
||||
volume_gb?: number;
|
||||
subnet_id?: string;
|
||||
security_group_ids?: string[];
|
||||
key_pair_name?: string;
|
||||
assign_public_ip?: boolean;
|
||||
}
|
||||
|
||||
export interface Deployment {
|
||||
_id?: string;
|
||||
name: string;
|
||||
@@ -327,9 +337,10 @@ export interface Log {
|
||||
export interface User {
|
||||
_id?: string;
|
||||
username: string;
|
||||
enabled: boolean;
|
||||
admin: boolean;
|
||||
create_server_permissions: boolean;
|
||||
enabled?: boolean;
|
||||
admin?: boolean;
|
||||
create_server_permissions?: boolean;
|
||||
create_build_permissions?: boolean;
|
||||
avatar?: string;
|
||||
secrets?: ApiSecret[];
|
||||
password?: string;
|
||||
@@ -382,7 +393,6 @@ export enum Operation {
|
||||
UpdateBuild = "update_build",
|
||||
DeleteBuild = "delete_build",
|
||||
BuildBuild = "build_build",
|
||||
RecloneBuild = "reclone_build",
|
||||
CreateDeployment = "create_deployment",
|
||||
UpdateDeployment = "update_deployment",
|
||||
DeleteDeployment = "delete_deployment",
|
||||
@@ -449,7 +459,6 @@ export enum ProcedureOperation {
|
||||
PruneContainersServer = "prune_containers_server",
|
||||
PruneNetworksServer = "prune_networks_server",
|
||||
BuildBuild = "build_build",
|
||||
RecloneBuild = "reclone_build",
|
||||
DeployContainer = "deploy_container",
|
||||
StopContainer = "stop_container",
|
||||
StartContainer = "start_container",
|
||||
|
||||
@@ -2,16 +2,12 @@
|
||||
Generated by typeshare 1.0.0
|
||||
*/
|
||||
|
||||
import { PermissionLevel, PermissionsTarget } from "../types";
|
||||
|
||||
export interface CreateBuildBody {
|
||||
name: string;
|
||||
server_id: string;
|
||||
}
|
||||
|
||||
export interface CopyBuildBody {
|
||||
name: string;
|
||||
server_id: string;
|
||||
}
|
||||
|
||||
export interface BuildVersionsQuery {
|
||||
|
||||
@@ -88,7 +88,6 @@ pub struct Build {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct BuildActionState {
|
||||
pub building: bool,
|
||||
pub recloning: bool,
|
||||
pub updating: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ impl Busy for DeploymentActionState {
|
||||
|
||||
impl Busy for BuildActionState {
|
||||
fn busy(&self) -> bool {
|
||||
self.building || self.recloning || self.updating
|
||||
self.building || self.updating
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user