mirror of
https://github.com/moghtech/komodo.git
synced 2026-09-11 08:01:13 +00:00
improve config
This commit is contained in:
@@ -4,7 +4,7 @@ import {
|
||||
ConfirmUpdate,
|
||||
} from "@components/config/util";
|
||||
import { Section } from "@components/layouts";
|
||||
import { snake_case_to_upper_space_case } from "@lib/formatting";
|
||||
import { cn } from "@lib/utils";
|
||||
import { Types } from "@monitor/client";
|
||||
import { Button } from "@ui/button";
|
||||
import { Card, CardHeader, CardTitle, CardContent } from "@ui/card";
|
||||
@@ -73,6 +73,21 @@ export const ConfigLayout = <
|
||||
|
||||
type PrimitiveConfigArgs = { placeholder: string };
|
||||
|
||||
type ConfigComponent<T> = {
|
||||
label: string;
|
||||
icon?: ReactNode;
|
||||
actions?: ReactNode;
|
||||
hidden?: boolean;
|
||||
labelHidden?: boolean;
|
||||
contentHidden?: boolean;
|
||||
components: {
|
||||
[K in keyof Partial<T>]:
|
||||
| boolean
|
||||
| PrimitiveConfigArgs
|
||||
| ((value: T[K], set: (value: Partial<T>) => void) => ReactNode);
|
||||
};
|
||||
};
|
||||
|
||||
export const Config = <T,>({
|
||||
config,
|
||||
update,
|
||||
@@ -91,16 +106,8 @@ export const Config = <T,>({
|
||||
selector?: ReactNode;
|
||||
titleOther?: ReactNode;
|
||||
components: Record<
|
||||
string,
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
[K in keyof Partial<T>]:
|
||||
| boolean
|
||||
| PrimitiveConfigArgs
|
||||
| ((value: T[K], set: (value: Partial<T>) => void) => ReactNode);
|
||||
}
|
||||
>
|
||||
string, // sidebar key
|
||||
ConfigComponent<T>[]
|
||||
>;
|
||||
}) => {
|
||||
const [show, setShow] = useState(keys(components)[0]);
|
||||
@@ -136,6 +143,7 @@ export const Config = <T,>({
|
||||
}
|
||||
>
|
||||
<div className="flex gap-4">
|
||||
{/** The sidebar when large */}
|
||||
<div className="hidden lg:flex flex-col gap-4 w-[300px]">
|
||||
{keys(components).map((tab) => (
|
||||
<Button
|
||||
@@ -148,25 +156,53 @@ export const Config = <T,>({
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-6 min-h-[500px] w-full">
|
||||
{Object.entries(components[show]).map(([k, v]) => (
|
||||
<Card className="w-full" key={k}>
|
||||
{k && (
|
||||
<CardHeader className="border-b">
|
||||
<CardTitle>{snake_case_to_upper_space_case(k)}</CardTitle>
|
||||
</CardHeader>
|
||||
)}
|
||||
<CardContent className="flex flex-col gap-2 mt-4">
|
||||
<ConfigAgain
|
||||
config={config}
|
||||
update={update}
|
||||
set={(u) => set((p) => ({ ...p, ...u }))}
|
||||
components={v}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
{components[show].map(
|
||||
({
|
||||
label,
|
||||
labelHidden,
|
||||
icon,
|
||||
actions,
|
||||
hidden,
|
||||
contentHidden,
|
||||
components,
|
||||
}) =>
|
||||
!hidden && (
|
||||
<Card className="w-full grid gap-2" key={label}>
|
||||
{!labelHidden && (
|
||||
<CardHeader
|
||||
className={cn(
|
||||
"flex-row items-center justify-between w-full py-0 h-[60px] space-y-0",
|
||||
!contentHidden && "border-b"
|
||||
)}
|
||||
>
|
||||
<CardTitle className="flex gap-4">
|
||||
{icon}
|
||||
{label}
|
||||
</CardTitle>
|
||||
{actions}
|
||||
</CardHeader>
|
||||
)}
|
||||
{!contentHidden && (
|
||||
<CardContent
|
||||
className={cn(
|
||||
"flex flex-col gap-2 pb-3",
|
||||
labelHidden && "pt-3"
|
||||
)}
|
||||
>
|
||||
<ConfigAgain
|
||||
config={config}
|
||||
update={update}
|
||||
set={(u) => set((p) => ({ ...p, ...u }))}
|
||||
components={components}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</CardContent>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ConfigLayout>
|
||||
@@ -200,10 +236,13 @@ export const ConfigAgain = <
|
||||
const value = update[key] ?? config[key];
|
||||
if (typeof component === "function") {
|
||||
return (
|
||||
<Fragment key={key.toString()}>{component?.(value, set)}</Fragment>
|
||||
<Fragment key={key.toString()}>{component(value, set)}</Fragment>
|
||||
);
|
||||
} else if (typeof component === "object" || component === true) {
|
||||
const args = (typeof component === "object") ? component as PrimitiveConfigArgs : undefined;
|
||||
const args =
|
||||
typeof component === "object"
|
||||
? (component as PrimitiveConfigArgs)
|
||||
: undefined;
|
||||
switch (typeof value) {
|
||||
case "string":
|
||||
return (
|
||||
@@ -245,7 +284,6 @@ export const ConfigAgain = <
|
||||
} else if (component === false) {
|
||||
return <Fragment key={key.toString()} />;
|
||||
}
|
||||
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -104,11 +104,12 @@ export const DoubleInput = <
|
||||
leftpl,
|
||||
rightval,
|
||||
rightpl,
|
||||
addName,
|
||||
// addName,
|
||||
onLeftChange,
|
||||
onRightChange,
|
||||
onAdd,
|
||||
// onAdd,
|
||||
onRemove,
|
||||
containerClassName,
|
||||
inputClassName,
|
||||
}: {
|
||||
disabled: boolean;
|
||||
@@ -117,15 +118,16 @@ export const DoubleInput = <
|
||||
leftpl: string;
|
||||
rightval: R;
|
||||
rightpl: string;
|
||||
addName: string;
|
||||
// addName: string;
|
||||
onLeftChange: (value: T[L], i: number) => void;
|
||||
onRightChange: (value: T[R], i: number) => void;
|
||||
onAdd: () => void;
|
||||
// onAdd: () => void;
|
||||
onRemove: (i: number) => void;
|
||||
containerClassName?: string;
|
||||
inputClassName?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className={cn("flex flex-col gap-4", containerClassName)}>
|
||||
{values?.map((value, i) => (
|
||||
<div className="flex items-center justify-between gap-4" key={i}>
|
||||
<Input
|
||||
@@ -150,7 +152,7 @@ export const DoubleInput = <
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{!disabled && (
|
||||
{/* {!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="flex items-center gap-2 w-[200px] place-self-end"
|
||||
@@ -159,7 +161,7 @@ export const DoubleInput = <
|
||||
<PlusCircle className="w-4 h-4" />
|
||||
Add {addName}
|
||||
</Button>
|
||||
)}
|
||||
)} */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -36,12 +36,15 @@ export const AlerterConfig = ({ id }: { id: string }) => {
|
||||
await mutateAsync({ id, config: { type, params: update } });
|
||||
}}
|
||||
components={{
|
||||
general: {
|
||||
general: {
|
||||
url: true,
|
||||
enabled: true,
|
||||
general: [
|
||||
{
|
||||
label: "General",
|
||||
components: {
|
||||
url: true,
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
selector={
|
||||
<div className="flex gap-2 items-center text-sm">
|
||||
|
||||
@@ -44,135 +44,153 @@ export const BuildConfig = ({ id }: { id: string }) => {
|
||||
await mutateAsync({ id, config: update });
|
||||
}}
|
||||
components={{
|
||||
general: {
|
||||
general: {
|
||||
version: (version, set) => {
|
||||
const { major, minor, patch } = version ?? {
|
||||
major: 0,
|
||||
minor: 0,
|
||||
patch: 0,
|
||||
};
|
||||
return (
|
||||
<ConfigItem label="Version">
|
||||
<div className="flex gap-4 items-center">
|
||||
<div className="text-xl">
|
||||
v{major}.{minor}.{patch}
|
||||
general: [
|
||||
{
|
||||
label: "General",
|
||||
components: {
|
||||
version: (version, set) => {
|
||||
const { major, minor, patch } = version ?? {
|
||||
major: 0,
|
||||
minor: 0,
|
||||
patch: 0,
|
||||
};
|
||||
return (
|
||||
<ConfigItem label="Version">
|
||||
<div className="flex gap-4 items-center">
|
||||
<div className="text-xl">
|
||||
v{major}.{minor}.{patch}
|
||||
</div>
|
||||
{!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
set({
|
||||
version: { major: major + 1, minor, patch: 0 },
|
||||
})
|
||||
}
|
||||
>
|
||||
+ Major
|
||||
</Button>
|
||||
)}
|
||||
{!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
set({
|
||||
version: { major, minor: minor + 1, patch: 0 },
|
||||
})
|
||||
}
|
||||
>
|
||||
+ Minor
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
set({
|
||||
version: { major: major + 1, minor, patch: 0 },
|
||||
})
|
||||
}
|
||||
>
|
||||
+ Major
|
||||
</Button>
|
||||
)}
|
||||
{!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
set({
|
||||
version: { major, minor: minor + 1, patch: 0 },
|
||||
})
|
||||
}
|
||||
>
|
||||
+ Minor
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</ConfigItem>
|
||||
);
|
||||
},
|
||||
builder_id: (id, set) => (
|
||||
<ConfigItem label="Builder">
|
||||
<ResourceSelector
|
||||
type="Builder"
|
||||
selected={id}
|
||||
onSelect={(builder_id) => set({ builder_id })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</ConfigItem>
|
||||
);
|
||||
),
|
||||
},
|
||||
builder_id: (id, set) => (
|
||||
<ConfigItem label="Builder">
|
||||
<ResourceSelector
|
||||
},
|
||||
{
|
||||
label: "Git",
|
||||
components: {
|
||||
repo: { placeholder: "Enter repo" },
|
||||
branch: { placeholder: "Enter branch" },
|
||||
commit: { placeholder: "Enter specific commit hash. Optional." },
|
||||
github_account: (account, set) => (
|
||||
<AccountSelector
|
||||
id={update.builder_id ?? config.builder_id ?? undefined}
|
||||
type="Builder"
|
||||
selected={id}
|
||||
onSelect={(builder_id) => set({ builder_id })}
|
||||
account_type="github"
|
||||
selected={account}
|
||||
onSelect={(github_account) => set({ github_account })}
|
||||
disabled={disabled}
|
||||
placeholder="None"
|
||||
/>
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Docker",
|
||||
components: {
|
||||
build_path: true,
|
||||
dockerfile_path: true,
|
||||
docker_account: (account, set) => (
|
||||
<AccountSelector
|
||||
id={update.builder_id ?? config.builder_id ?? undefined}
|
||||
type="Builder"
|
||||
account_type="docker"
|
||||
selected={account}
|
||||
onSelect={(docker_account) => set({ docker_account })}
|
||||
disabled={disabled}
|
||||
placeholder="None"
|
||||
/>
|
||||
),
|
||||
docker_organization:
|
||||
docker_organizations === undefined ||
|
||||
docker_organizations.length === 0
|
||||
? undefined
|
||||
: (value, set) => (
|
||||
<DockerOrganizations
|
||||
value={value}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
use_buildx: true,
|
||||
labels: (l, set) => (
|
||||
<LabelsConfig labels={l ?? []} set={set} disabled={disabled} />
|
||||
),
|
||||
extra_args: (value, set) => (
|
||||
<ExtraArgs args={value ?? []} set={set} disabled={disabled} />
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Pre Build",
|
||||
components: {
|
||||
pre_build: (value, set) => (
|
||||
<SystemCommand
|
||||
label="Pre Build"
|
||||
value={value}
|
||||
set={(value) => set({ pre_build: value })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</ConfigItem>
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
git: {
|
||||
repo: { placeholder: "Enter repo" },
|
||||
branch: { placeholder: "Enter branch" },
|
||||
commit: { placeholder: "Enter specific commit hash. Optional." },
|
||||
github_account: (account, set) => (
|
||||
<AccountSelector
|
||||
id={update.builder_id ?? config.builder_id ?? undefined}
|
||||
type="Builder"
|
||||
account_type="github"
|
||||
selected={account}
|
||||
onSelect={(github_account) => set({ github_account })}
|
||||
disabled={disabled}
|
||||
placeholder="None"
|
||||
/>
|
||||
),
|
||||
{
|
||||
label: "Github Webhook",
|
||||
components: {
|
||||
["build" as any]: () => (
|
||||
<ConfigItem label="Build">
|
||||
<CopyGithubWebhook path={`/build/${id}`} />
|
||||
</ConfigItem>
|
||||
),
|
||||
webhook_enabled: true,
|
||||
},
|
||||
},
|
||||
docker: {
|
||||
build_path: true,
|
||||
dockerfile_path: true,
|
||||
docker_account: (account, set) => (
|
||||
<AccountSelector
|
||||
id={update.builder_id ?? config.builder_id ?? undefined}
|
||||
type="Builder"
|
||||
account_type="docker"
|
||||
selected={account}
|
||||
onSelect={(docker_account) => set({ docker_account })}
|
||||
disabled={disabled}
|
||||
placeholder="None"
|
||||
/>
|
||||
),
|
||||
docker_organization:
|
||||
docker_organizations === undefined ||
|
||||
docker_organizations.length === 0
|
||||
? undefined
|
||||
: (value, set) => (
|
||||
<DockerOrganizations
|
||||
value={value}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
use_buildx: true,
|
||||
labels: (l, set) => (
|
||||
<LabelsConfig labels={l ?? []} set={set} disabled={disabled} />
|
||||
),
|
||||
extra_args: (value, set) => (
|
||||
<ExtraArgs args={value ?? []} set={set} disabled={disabled} />
|
||||
),
|
||||
],
|
||||
"Build Args": [
|
||||
{
|
||||
label: "Build Args",
|
||||
components: {
|
||||
build_args: (vars, set) => (
|
||||
<BuildArgs vars={vars ?? []} set={set} disabled={disabled} />
|
||||
),
|
||||
skip_secret_interp: true,
|
||||
},
|
||||
},
|
||||
pre_build: {
|
||||
pre_build: (value, set) => (
|
||||
<SystemCommand
|
||||
label="Pre Build"
|
||||
value={value}
|
||||
set={(value) => set({ pre_build: value })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
},
|
||||
github_webhook: {
|
||||
["build" as any]: () => (
|
||||
<ConfigItem label="Build">
|
||||
<CopyGithubWebhook path={`/build/${id}`} />
|
||||
</ConfigItem>
|
||||
),
|
||||
webhook_enabled: true,
|
||||
},
|
||||
},
|
||||
"Build Args": {
|
||||
"Build Args": {
|
||||
build_args: (vars, set) => (
|
||||
<BuildArgs vars={vars ?? []} set={set} disabled={disabled} />
|
||||
),
|
||||
skip_secret_interp: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -32,43 +32,46 @@ const AwsBuilderConfig = ({ id }: { id: string }) => {
|
||||
await mutateAsync({ id, config: { type: "Aws", params: update } });
|
||||
}}
|
||||
components={{
|
||||
general: {
|
||||
general: {
|
||||
region: true,
|
||||
instance_type: true,
|
||||
volume_gb: true,
|
||||
ami_id: true,
|
||||
subnet_id: true,
|
||||
key_pair_name: true,
|
||||
assign_public_ip: true,
|
||||
use_public_ip: true,
|
||||
security_group_ids: (values, set) => (
|
||||
<InputList
|
||||
field="security_group_ids"
|
||||
values={values}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
github_accounts: (accounts, set) => (
|
||||
<InputList
|
||||
field="github_accounts"
|
||||
values={accounts ?? []}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
docker_accounts: (accounts, set) => (
|
||||
<InputList
|
||||
field="docker_accounts"
|
||||
values={accounts ?? []}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
port: true,
|
||||
general: [
|
||||
{
|
||||
label: "General",
|
||||
components: {
|
||||
region: true,
|
||||
instance_type: true,
|
||||
volume_gb: true,
|
||||
ami_id: true,
|
||||
subnet_id: true,
|
||||
key_pair_name: true,
|
||||
assign_public_ip: true,
|
||||
use_public_ip: true,
|
||||
security_group_ids: (values, set) => (
|
||||
<InputList
|
||||
field="security_group_ids"
|
||||
values={values}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
github_accounts: (accounts, set) => (
|
||||
<InputList
|
||||
field="github_accounts"
|
||||
values={accounts ?? []}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
docker_accounts: (accounts, set) => (
|
||||
<InputList
|
||||
field="docker_accounts"
|
||||
values={accounts ?? []}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
port: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -95,21 +98,24 @@ const ServerBuilderConfig = ({ id }: { id: string }) => {
|
||||
await mutateAsync({ id, config: { type: "Server", params: update } });
|
||||
}}
|
||||
components={{
|
||||
general: {
|
||||
general: {
|
||||
server_id: (id, set) => (
|
||||
<div className="flex justify-between items-center border-b pb-4">
|
||||
Select Server
|
||||
<ResourceSelector
|
||||
type="Server"
|
||||
selected={id}
|
||||
onSelect={(server_id) => set({ server_id })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
general: [
|
||||
{
|
||||
label: "General",
|
||||
components: {
|
||||
server_id: (id, set) => (
|
||||
<div className="flex justify-between items-center border-b pb-4">
|
||||
Select Server
|
||||
<ResourceSelector
|
||||
type="Server"
|
||||
selected={id}
|
||||
onSelect={(server_id) => set({ server_id })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -81,11 +81,13 @@ export const ResourceSelector = ({
|
||||
selected,
|
||||
onSelect,
|
||||
disabled,
|
||||
align,
|
||||
}: {
|
||||
type: UsableResource;
|
||||
selected: string | undefined;
|
||||
onSelect?: (id: string) => void;
|
||||
disabled?: boolean;
|
||||
align?: "start" | "center" | "end"
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [input, setInput] = useState("");
|
||||
@@ -103,7 +105,7 @@ export const ResourceSelector = ({
|
||||
{!disabled && <ChevronsUpDown className="w-3 h-3" />}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[200px] max-h-[200px] p-0" sideOffset={12}>
|
||||
<PopoverContent className="w-[200px] max-h-[200px] p-0" align={align}>
|
||||
<Command>
|
||||
<CommandInput
|
||||
placeholder={`Search ${type}s`}
|
||||
@@ -284,18 +286,16 @@ export const LabelsConfig = ({
|
||||
set: (input: Partial<Types.DeploymentConfig | Types.BuildConfig>) => void;
|
||||
disabled: boolean;
|
||||
}) => (
|
||||
<ConfigItem
|
||||
label="Labels"
|
||||
className={labels.length > 0 ? "items-start" : undefined}
|
||||
>
|
||||
<div className="py-2 w-full flex justify-end">
|
||||
<DoubleInput
|
||||
disabled={disabled}
|
||||
inputClassName="max-w-full"
|
||||
containerClassName="w-fit"
|
||||
values={labels}
|
||||
leftval="variable"
|
||||
leftpl="Key"
|
||||
rightval="value"
|
||||
rightpl="Value"
|
||||
addName="Label"
|
||||
onLeftChange={(variable, i) => {
|
||||
labels[i].variable = variable;
|
||||
set({ labels: [...labels] });
|
||||
@@ -304,14 +304,11 @@ export const LabelsConfig = ({
|
||||
labels[i].value = value;
|
||||
set({ labels: [...labels] });
|
||||
}}
|
||||
onAdd={() =>
|
||||
set({ labels: [...(labels ?? []), { variable: "", value: "" }] })
|
||||
}
|
||||
onRemove={(idx) =>
|
||||
set({ labels: [...labels.filter((_, i) => i !== idx)] })
|
||||
}
|
||||
/>
|
||||
</ConfigItem>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const CopyGithubWebhook = ({
|
||||
@@ -333,10 +330,12 @@ export const ServerSelector = ({
|
||||
selected,
|
||||
set,
|
||||
disabled,
|
||||
align,
|
||||
}: {
|
||||
selected: string | undefined;
|
||||
set: (input: Partial<Types.DeploymentConfig>) => void;
|
||||
disabled: boolean;
|
||||
align?: "start" | "center" | "end"
|
||||
}) => (
|
||||
<ConfigItem label="Server">
|
||||
<ResourceSelector
|
||||
@@ -344,6 +343,7 @@ export const ServerSelector = ({
|
||||
selected={selected}
|
||||
onSelect={(server_id) => set({ server_id })}
|
||||
disabled={disabled}
|
||||
align={align}
|
||||
/>
|
||||
</ConfigItem>
|
||||
);
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ConfigItem } from "@components/config/util";
|
||||
import { useRead } from "@lib/hooks";
|
||||
import { Types } from "@monitor/client";
|
||||
import { Button } from "@ui/button";
|
||||
@@ -25,50 +24,46 @@ export const ExtraArgs = ({
|
||||
disabled: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<ConfigItem
|
||||
label="Extra Args"
|
||||
className={args.length > 0 ? "items-start" : undefined}
|
||||
>
|
||||
<div className="flex flex-col gap-4 w-full max-w-[400px]">
|
||||
{args.map((arg, i) => (
|
||||
<div className="w-full flex gap-4" key={i}>
|
||||
<Input
|
||||
value={arg}
|
||||
placeholder="--extra-arg=value"
|
||||
onChange={(e) => {
|
||||
args[i] = e.target.value;
|
||||
set({ extra_args: [...args] });
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
set({ extra_args: [...args.filter((_, idx) => idx !== i)] })
|
||||
}
|
||||
>
|
||||
<MinusCircle className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{!disabled && (
|
||||
<div className="place-self-end">
|
||||
<AddExtraArgMenu
|
||||
onSelect={(suggestion) =>
|
||||
set({ extra_args: [...args, suggestion] })
|
||||
<div className="flex flex-col justify-end gap-4 w-full">
|
||||
{args.map((arg, i) => (
|
||||
<div className="w-full flex gap-4 justify-end" key={i}>
|
||||
<Input
|
||||
value={arg}
|
||||
placeholder="--extra-arg=value"
|
||||
onChange={(e) => {
|
||||
args[i] = e.target.value;
|
||||
set({ extra_args: [...args] });
|
||||
}}
|
||||
disabled={disabled}
|
||||
className="w-[400px] max-w-full"
|
||||
/>
|
||||
{!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
set({ extra_args: [...args.filter((_, idx) => idx !== i)] })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ConfigItem>
|
||||
>
|
||||
<MinusCircle className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* {!disabled && (
|
||||
<div className="place-self-end">
|
||||
<AddExtraArgMenu
|
||||
onSelect={(suggestion) =>
|
||||
set({ extra_args: [...args, suggestion] })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const AddExtraArgMenu = ({
|
||||
export const AddExtraArgMenu = ({
|
||||
onSelect,
|
||||
}: {
|
||||
onSelect: (suggestion: string) => void;
|
||||
@@ -86,7 +81,7 @@ const AddExtraArgMenu = ({
|
||||
<PlusCircle className="w-4 h-4" /> Add Extra Arg
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[300px] max-h-[400px] p-0">
|
||||
<PopoverContent className="w-[300px] max-h-[400px] p-0" align="end">
|
||||
<Command>
|
||||
<CommandInput
|
||||
placeholder="Search suggestions"
|
||||
@@ -102,7 +97,10 @@ const AddExtraArgMenu = ({
|
||||
|
||||
<CommandGroup>
|
||||
<CommandItem
|
||||
onSelect={() => onSelect("")}
|
||||
onSelect={() => {
|
||||
onSelect("");
|
||||
setOpen(false);
|
||||
}}
|
||||
className="w-full cursor-pointer"
|
||||
>
|
||||
Empty Extra Arg
|
||||
@@ -111,7 +109,10 @@ const AddExtraArgMenu = ({
|
||||
{suggestions?.map((suggestion) => (
|
||||
<CommandItem
|
||||
key={suggestion}
|
||||
onSelect={() => onSelect(suggestion)}
|
||||
onSelect={() => {
|
||||
onSelect(suggestion);
|
||||
setOpen(false);
|
||||
}}
|
||||
className="w-full overflow-hidden overflow-ellipsis cursor-pointer"
|
||||
>
|
||||
{suggestion}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ConfigItem, DoubleInput } from "@components/config/util";
|
||||
import { DoubleInput } from "@components/config/util";
|
||||
import { Types } from "@monitor/client";
|
||||
|
||||
export const PortsConfig = ({
|
||||
@@ -10,18 +10,16 @@ export const PortsConfig = ({
|
||||
set: (input: Partial<Types.DeploymentConfig>) => void;
|
||||
disabled: boolean;
|
||||
}) => (
|
||||
<ConfigItem
|
||||
label="Ports"
|
||||
className={ports.length > 0 ? "items-start" : undefined}
|
||||
>
|
||||
<div className="py-2 w-full flex justify-end">
|
||||
<DoubleInput
|
||||
disabled={disabled}
|
||||
inputClassName="w-[200px] max-w-full"
|
||||
containerClassName="w-fit"
|
||||
values={ports}
|
||||
leftval="local"
|
||||
leftpl="Local"
|
||||
rightval="container"
|
||||
rightpl="Container"
|
||||
addName="Port"
|
||||
onLeftChange={(local, i) => {
|
||||
ports[i].local = local;
|
||||
set({ ports: [...ports] });
|
||||
@@ -30,10 +28,7 @@ export const PortsConfig = ({
|
||||
ports[i].container = container;
|
||||
set({ ports: [...ports] });
|
||||
}}
|
||||
onAdd={() =>
|
||||
set({ ports: [...(ports ?? []), { container: "", local: "" }] })
|
||||
}
|
||||
onRemove={(idx) => set({ ports: [...ports.filter((_, i) => i !== idx)] })}
|
||||
/>
|
||||
</ConfigItem>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ConfigItem, DoubleInput } from "@components/config/util";
|
||||
import { DoubleInput } from "@components/config/util";
|
||||
import { Types } from "@monitor/client";
|
||||
|
||||
export const VolumesConfig = ({
|
||||
@@ -10,19 +10,16 @@ export const VolumesConfig = ({
|
||||
set: (input: Partial<Types.DeploymentConfig>) => void;
|
||||
disabled: boolean;
|
||||
}) => (
|
||||
<ConfigItem
|
||||
label="Volumes"
|
||||
className={volumes.length > 0 ? "items-start" : undefined}
|
||||
>
|
||||
<div className="py-2 w-full flex justify-end">
|
||||
<DoubleInput
|
||||
disabled={disabled}
|
||||
inputClassName="w-[300px] max-w-full"
|
||||
inputClassName="w-[400px] max-w-full"
|
||||
containerClassName="w-fit"
|
||||
values={volumes}
|
||||
leftval="local"
|
||||
leftpl="Local"
|
||||
rightval="container"
|
||||
rightpl="Container"
|
||||
addName="Volume"
|
||||
onLeftChange={(local, i) => {
|
||||
volumes[i].local = local;
|
||||
set({ volumes: [...volumes] });
|
||||
@@ -31,12 +28,9 @@ export const VolumesConfig = ({
|
||||
volumes[i].container = container;
|
||||
set({ volumes: [...volumes] });
|
||||
}}
|
||||
onAdd={() =>
|
||||
set({ volumes: [...(volumes ?? []), { container: "", local: "" }] })
|
||||
}
|
||||
onRemove={(idx) =>
|
||||
set({ volumes: [...volumes.filter((_, i) => i !== idx)] })
|
||||
}
|
||||
/>
|
||||
</ConfigItem>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@ import { NetworkModeSelector } from "./components/network";
|
||||
import { PortsConfig } from "./components/ports";
|
||||
import { EnvVars } from "./components/environment";
|
||||
import { VolumesConfig } from "./components/volumes";
|
||||
import { ExtraArgs } from "./components/extra-args";
|
||||
import { AddExtraArgMenu, ExtraArgs } from "./components/extra-args";
|
||||
import { Config } from "@components/config";
|
||||
import {
|
||||
DefaultTerminationSignal,
|
||||
@@ -17,8 +17,16 @@ import {
|
||||
} from "./components/term-signal";
|
||||
import { LabelsConfig, ServerSelector } from "@components/resources/common";
|
||||
import { TextUpdateMenu } from "@components/util";
|
||||
import { Button } from "@ui/button";
|
||||
import { PlusCircle } from "lucide-react";
|
||||
|
||||
export const DeploymentConfig = ({ id, titleOther }: { id: string; titleOther: ReactNode }) => {
|
||||
export const DeploymentConfig = ({
|
||||
id,
|
||||
titleOther,
|
||||
}: {
|
||||
id: string;
|
||||
titleOther: ReactNode;
|
||||
}) => {
|
||||
const perms = useRead("GetPermissionLevel", {
|
||||
target: { type: "Deployment", id },
|
||||
}).data;
|
||||
@@ -28,10 +36,10 @@ export const DeploymentConfig = ({ id, titleOther }: { id: string; titleOther: R
|
||||
|
||||
if (!config) return null;
|
||||
|
||||
const show_ports = update.network
|
||||
? update.network !== "host"
|
||||
const hide_ports = update.network
|
||||
? update.network === "host" || update.network === "none"
|
||||
: config.network
|
||||
? config.network !== "host"
|
||||
? config.network === "host" || config.network === "none"
|
||||
: false;
|
||||
|
||||
const disabled = perms !== Types.PermissionLevel.Write;
|
||||
@@ -47,111 +55,226 @@ export const DeploymentConfig = ({ id, titleOther }: { id: string; titleOther: R
|
||||
await mutateAsync({ id, config: update });
|
||||
}}
|
||||
components={{
|
||||
general: {
|
||||
"": {
|
||||
server_id: (value, set) => (
|
||||
<ServerSelector selected={value} set={set} disabled={disabled} />
|
||||
),
|
||||
general: [
|
||||
{
|
||||
label: "Server Id",
|
||||
labelHidden: true,
|
||||
components: {
|
||||
server_id: (value, set) => (
|
||||
<ServerSelector
|
||||
selected={value}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
align="end"
|
||||
/>
|
||||
),
|
||||
},
|
||||
},
|
||||
container: {
|
||||
image: (value, set) => (
|
||||
<ImageConfig image={value} set={set} disabled={disabled} />
|
||||
),
|
||||
docker_account: (value, set) => (
|
||||
<AccountSelector
|
||||
id={update.server_id ?? config.server_id}
|
||||
account_type="docker"
|
||||
type="Server"
|
||||
selected={value}
|
||||
onSelect={(docker_account) => set({ docker_account })}
|
||||
disabled={disabled}
|
||||
placeholder={
|
||||
(update.image?.type || config.image?.type) === "Build"
|
||||
? "Same as build"
|
||||
: "None"
|
||||
{
|
||||
label: "Container",
|
||||
components: {
|
||||
image: (value, set) => (
|
||||
<ImageConfig image={value} set={set} disabled={disabled} />
|
||||
),
|
||||
docker_account: (value, set) => (
|
||||
<AccountSelector
|
||||
id={update.server_id ?? config.server_id}
|
||||
account_type="docker"
|
||||
type="Server"
|
||||
selected={value}
|
||||
onSelect={(docker_account) => set({ docker_account })}
|
||||
disabled={disabled}
|
||||
placeholder={
|
||||
(update.image?.type || config.image?.type) === "Build"
|
||||
? "Same as build"
|
||||
: "None"
|
||||
}
|
||||
/>
|
||||
),
|
||||
restart: (value, set) => (
|
||||
<RestartModeSelector
|
||||
selected={value}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
network: (value, set) => (
|
||||
<NetworkModeSelector
|
||||
server_id={update.server_id ?? config.server_id}
|
||||
selected={value}
|
||||
onSelect={(network) => set({ network })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
command: (value, set) => (
|
||||
<ConfigItem label="Command">
|
||||
<TextUpdateMenu
|
||||
title="Update Command"
|
||||
placeholder="Set custom command"
|
||||
value={value}
|
||||
onUpdate={(command) => set({ command })}
|
||||
triggerClassName="min-w-[300px] max-w-[400px]"
|
||||
/>
|
||||
</ConfigItem>
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Ports",
|
||||
hidden: hide_ports,
|
||||
contentHidden: (update.ports ?? config.ports)?.length === 0,
|
||||
actions: (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
set({
|
||||
...update,
|
||||
ports: [
|
||||
...(update.ports ?? config.ports ?? []),
|
||||
{ container: "", local: "" },
|
||||
],
|
||||
})
|
||||
}
|
||||
/>
|
||||
className="flex items-center gap-2 w-[200px]"
|
||||
>
|
||||
<PlusCircle className="w-4 h-4" />
|
||||
Add Port
|
||||
</Button>
|
||||
),
|
||||
restart: (value, set) => (
|
||||
<RestartModeSelector
|
||||
selected={value}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
network: (value, set) => (
|
||||
<NetworkModeSelector
|
||||
server_id={update.server_id ?? config.server_id}
|
||||
selected={value}
|
||||
onSelect={(network) => set({ network })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
ports: (value, set) =>
|
||||
show_ports && (
|
||||
components: {
|
||||
ports: (value, set) => (
|
||||
<PortsConfig
|
||||
ports={value ?? []}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
volumes: (v, set) => (
|
||||
<VolumesConfig volumes={v ?? []} set={set} disabled={disabled} />
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Volumes",
|
||||
contentHidden: (update.volumes ?? config.volumes)?.length === 0,
|
||||
actions: (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
set({
|
||||
...update,
|
||||
volumes: [
|
||||
...(update.volumes ?? config.volumes ?? []),
|
||||
{ container: "", local: "" },
|
||||
],
|
||||
})
|
||||
}
|
||||
className="flex items-center gap-2 w-[200px]"
|
||||
>
|
||||
<PlusCircle className="w-4 h-4" />
|
||||
Add Volume
|
||||
</Button>
|
||||
),
|
||||
labels: (l, set) => (
|
||||
<LabelsConfig labels={l ?? []} set={set} disabled={disabled} />
|
||||
),
|
||||
extra_args: (value, set) => (
|
||||
<ExtraArgs args={value ?? []} set={set} disabled={disabled} />
|
||||
),
|
||||
command: (value, set) => (
|
||||
<ConfigItem label="Command">
|
||||
<TextUpdateMenu
|
||||
title="Update Command"
|
||||
placeholder="Set custom command"
|
||||
value={value}
|
||||
onUpdate={(command) => set({ command })}
|
||||
triggerClassName="min-w-[300px] max-w-[400px]"
|
||||
components: {
|
||||
volumes: (v, set) => (
|
||||
<VolumesConfig
|
||||
volumes={v ?? []}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</ConfigItem>
|
||||
),
|
||||
),
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
send_alerts: true,
|
||||
redeploy_on_build:
|
||||
(update.image?.type || config.image?.type) === "Build",
|
||||
},
|
||||
},
|
||||
environment: {
|
||||
environment: {
|
||||
environment: (vars, set) => (
|
||||
<EnvVars
|
||||
vars={vars ?? []}
|
||||
set={set}
|
||||
server={update.server_id || config.server_id}
|
||||
disabled={disabled}
|
||||
{
|
||||
label: "Extra Args",
|
||||
contentHidden:
|
||||
(update.extra_args ?? config.extra_args)?.length === 0,
|
||||
actions: (
|
||||
<AddExtraArgMenu
|
||||
onSelect={(suggestion) =>
|
||||
set({
|
||||
extra_args: [
|
||||
...(update.extra_args ?? config.extra_args ?? []),
|
||||
suggestion,
|
||||
],
|
||||
})
|
||||
}
|
||||
/>
|
||||
),
|
||||
skip_secret_interp: true,
|
||||
components: {
|
||||
extra_args: (value, set) => (
|
||||
<ExtraArgs args={value ?? []} set={set} disabled={disabled} />
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
termination: {
|
||||
termination: {
|
||||
termination_signal: (value, set) => (
|
||||
<DefaultTerminationSignal
|
||||
arg={value}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
termination_timeout: (value, set) => (
|
||||
<TerminationTimeout arg={value} set={set} disabled={disabled} />
|
||||
),
|
||||
term_signal_labels: (value, set) => (
|
||||
<TermSignalLabels args={value} set={set} disabled={disabled} />
|
||||
{
|
||||
label: "Labels",
|
||||
actions: (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
set({
|
||||
...update,
|
||||
labels: [
|
||||
...(update.labels ?? config.labels ?? []),
|
||||
{ variable: "", value: "" },
|
||||
],
|
||||
})
|
||||
}
|
||||
className="flex items-center gap-2 w-[200px]"
|
||||
>
|
||||
<PlusCircle className="w-4 h-4" />
|
||||
Add Label
|
||||
</Button>
|
||||
),
|
||||
components: {
|
||||
labels: (l, set) => (
|
||||
<LabelsConfig labels={l ?? []} set={set} disabled={disabled} />
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Settings",
|
||||
components: {
|
||||
send_alerts: true,
|
||||
redeploy_on_build:
|
||||
(update.image?.type || config.image?.type) === "Build",
|
||||
},
|
||||
},
|
||||
],
|
||||
environment: [
|
||||
{
|
||||
label: "Environment",
|
||||
components: {
|
||||
environment: (vars, set) => (
|
||||
<EnvVars
|
||||
vars={vars ?? []}
|
||||
set={set}
|
||||
server={update.server_id || config.server_id}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
skip_secret_interp: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
termination: [
|
||||
{
|
||||
label: "Termination",
|
||||
components: {
|
||||
termination_signal: (value, set) => (
|
||||
<DefaultTerminationSignal
|
||||
arg={value}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
termination_timeout: (value, set) => (
|
||||
<TerminationTimeout arg={value} set={set} disabled={disabled} />
|
||||
),
|
||||
term_signal_labels: (value, set) => (
|
||||
<TermSignalLabels args={value} set={set} disabled={disabled} />
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -33,60 +33,74 @@ export const RepoConfig = ({ id }: { id: string }) => {
|
||||
await mutateAsync({ id, config: update });
|
||||
}}
|
||||
components={{
|
||||
general: {
|
||||
"": {
|
||||
server_id: (value, set) => (
|
||||
<ServerSelector selected={value} set={set} disabled={disabled} />
|
||||
),
|
||||
},
|
||||
general: {
|
||||
repo: { placeholder: "Enter repo" },
|
||||
branch: { placeholder: "Enter branch" },
|
||||
commit: { placeholder: "Enter specific commit hash. Optional." },
|
||||
github_account: (value, set) => {
|
||||
const server_id = update.server_id || config.server_id;
|
||||
if (server_id) {
|
||||
return (
|
||||
<GithubAccount
|
||||
server={server_id}
|
||||
value={value}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
general: [
|
||||
{
|
||||
label: "Server Id",
|
||||
labelHidden: true,
|
||||
components: {
|
||||
server_id: (value, set) => (
|
||||
<ServerSelector
|
||||
selected={value}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
},
|
||||
on_clone: (value, set) => (
|
||||
<SystemCommand
|
||||
label="On Clone"
|
||||
value={value}
|
||||
set={(value) => set({ on_clone: value })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
on_pull: (value, set) => (
|
||||
<SystemCommand
|
||||
label="On Pull"
|
||||
value={value}
|
||||
set={(value) => set({ on_pull: value })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
},
|
||||
github_webhooks: {
|
||||
["clone" as any]: () => (
|
||||
<ConfigItem label="Clone">
|
||||
<CopyGithubWebhook path={`/repo/${id}/clone`} />
|
||||
</ConfigItem>
|
||||
),
|
||||
["pull" as any]: () => (
|
||||
<ConfigItem label="Pull">
|
||||
<CopyGithubWebhook path={`/repo/${id}/pull`} />
|
||||
</ConfigItem>
|
||||
),
|
||||
webhook_enabled: true,
|
||||
{
|
||||
label: "General",
|
||||
components: {
|
||||
repo: { placeholder: "Enter repo" },
|
||||
branch: { placeholder: "Enter branch" },
|
||||
commit: { placeholder: "Enter specific commit hash. Optional." },
|
||||
github_account: (value, set) => {
|
||||
const server_id = update.server_id || config.server_id;
|
||||
if (server_id) {
|
||||
return (
|
||||
<GithubAccount
|
||||
server={server_id}
|
||||
value={value}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
on_clone: (value, set) => (
|
||||
<SystemCommand
|
||||
label="On Clone"
|
||||
value={value}
|
||||
set={(value) => set({ on_clone: value })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
on_pull: (value, set) => (
|
||||
<SystemCommand
|
||||
label="On Pull"
|
||||
value={value}
|
||||
set={(value) => set({ on_pull: value })}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Github Webhooks",
|
||||
components: {
|
||||
["clone" as any]: () => (
|
||||
<ConfigItem label="Clone">
|
||||
<CopyGithubWebhook path={`/repo/${id}/clone`} />
|
||||
</ConfigItem>
|
||||
),
|
||||
["pull" as any]: () => (
|
||||
<ConfigItem label="Pull">
|
||||
<CopyGithubWebhook path={`/repo/${id}/pull`} />
|
||||
</ConfigItem>
|
||||
),
|
||||
webhook_enabled: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -46,86 +46,91 @@ export const AwsServerTemplateConfig = ({ id }: { id: string }) => {
|
||||
await mutateAsync({ id, config: { type: "Aws", params: update } });
|
||||
}}
|
||||
components={{
|
||||
general: {
|
||||
general: {
|
||||
region: true,
|
||||
instance_type: true,
|
||||
ami_id: true,
|
||||
subnet_id: true,
|
||||
key_pair_name: true,
|
||||
assign_public_ip: true,
|
||||
use_public_ip: true,
|
||||
port: true,
|
||||
security_group_ids: (values, set) => (
|
||||
<InputList
|
||||
field="security_group_ids"
|
||||
values={values!}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
volumes: (volumes, set) => {
|
||||
return (
|
||||
<ConfigItem
|
||||
label="EBS Volumes"
|
||||
className={volumes.length > 0 ? "items-start" : undefined}
|
||||
>
|
||||
<div className="flex flex-col gap-4 w-full max-w-[400px]">
|
||||
{volumes.map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center justify-between gap-4"
|
||||
>
|
||||
<AwsVolumeDialog
|
||||
volumes={volumes}
|
||||
index={index}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
disabled={disabled}
|
||||
onClick={() =>
|
||||
set({
|
||||
volumes: volumes.filter((_, i) => i !== index),
|
||||
})
|
||||
}
|
||||
>
|
||||
<MinusCircle className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
set({
|
||||
volumes: [...volumes, newVolume(volumes.length)],
|
||||
})
|
||||
}
|
||||
>
|
||||
Add Volume
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</ConfigItem>
|
||||
);
|
||||
},
|
||||
user_data: (user_data, set) => (
|
||||
<ConfigItem label="User Data">
|
||||
<TextUpdateMenu
|
||||
title="Update User Data"
|
||||
placeholder="Set User Data"
|
||||
value={user_data}
|
||||
onUpdate={(user_data) => set({ user_data })}
|
||||
triggerClassName="min-w-[300px] max-w-[400px]"
|
||||
general: [
|
||||
{
|
||||
label: "General",
|
||||
components: {
|
||||
region: true,
|
||||
instance_type: true,
|
||||
ami_id: true,
|
||||
subnet_id: true,
|
||||
key_pair_name: true,
|
||||
assign_public_ip: true,
|
||||
use_public_ip: true,
|
||||
port: true,
|
||||
security_group_ids: (values, set) => (
|
||||
<InputList
|
||||
field="security_group_ids"
|
||||
values={values!}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</ConfigItem>
|
||||
),
|
||||
),
|
||||
volumes: (volumes, set) => {
|
||||
return (
|
||||
<ConfigItem
|
||||
label="EBS Volumes"
|
||||
className={volumes.length > 0 ? "items-start" : undefined}
|
||||
>
|
||||
<div className="flex flex-col gap-4 w-full max-w-[400px]">
|
||||
{volumes.map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center justify-between gap-4"
|
||||
>
|
||||
<AwsVolumeDialog
|
||||
volumes={volumes}
|
||||
index={index}
|
||||
set={set}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
disabled={disabled}
|
||||
onClick={() =>
|
||||
set({
|
||||
volumes: volumes.filter(
|
||||
(_, i) => i !== index
|
||||
),
|
||||
})
|
||||
}
|
||||
>
|
||||
<MinusCircle className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{!disabled && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
set({
|
||||
volumes: [...volumes, newVolume(volumes.length)],
|
||||
})
|
||||
}
|
||||
>
|
||||
Add Volume
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</ConfigItem>
|
||||
);
|
||||
},
|
||||
user_data: (user_data, set) => (
|
||||
<ConfigItem label="User Data">
|
||||
<TextUpdateMenu
|
||||
title="Update User Data"
|
||||
placeholder="Set User Data"
|
||||
value={user_data}
|
||||
onUpdate={(user_data) => set({ user_data })}
|
||||
triggerClassName="min-w-[300px] max-w-[400px]"
|
||||
/>
|
||||
</ConfigItem>
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -30,37 +30,52 @@ export const ServerConfig = ({ id }: { id: string }) => {
|
||||
await mutateAsync({ id, config: update });
|
||||
}}
|
||||
components={{
|
||||
general: {
|
||||
general: {
|
||||
address: true,
|
||||
region: true,
|
||||
enabled: true,
|
||||
stats_monitoring: true,
|
||||
auto_prune: true,
|
||||
general: [
|
||||
{
|
||||
label: "General",
|
||||
components: {
|
||||
address: true,
|
||||
region: true,
|
||||
enabled: true,
|
||||
stats_monitoring: true,
|
||||
auto_prune: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
alerts: {
|
||||
alerts: {
|
||||
send_unreachable_alerts: true,
|
||||
send_cpu_alerts: true,
|
||||
send_disk_alerts: true,
|
||||
send_mem_alerts: true,
|
||||
],
|
||||
alerts: [
|
||||
{
|
||||
label: "Alerts",
|
||||
components: {
|
||||
send_unreachable_alerts: true,
|
||||
send_cpu_alerts: true,
|
||||
send_disk_alerts: true,
|
||||
send_mem_alerts: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
warnings: {
|
||||
cpu: {
|
||||
cpu_warning: true,
|
||||
cpu_critical: true,
|
||||
],
|
||||
warnings: [
|
||||
{
|
||||
label: "Cpu",
|
||||
components: {
|
||||
cpu_warning: true,
|
||||
cpu_critical: true,
|
||||
},
|
||||
},
|
||||
memory: {
|
||||
mem_warning: true,
|
||||
mem_critical: true,
|
||||
{
|
||||
label: "Memory",
|
||||
components: {
|
||||
mem_warning: true,
|
||||
mem_critical: true,
|
||||
},
|
||||
},
|
||||
disk: {
|
||||
disk_warning: true,
|
||||
disk_critical: true,
|
||||
{
|
||||
label: "Disk",
|
||||
components: {
|
||||
disk_warning: true,
|
||||
disk_critical: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user