+
Volumes
-
+
{fields.map((field, idx) => (
-
+ 0,
'divide-red-500 border-red-500 dark:divide-red-500 dark:border-red-500':
control.getFieldState(
`services.${serviceIndex}.volumes.${idx}`,
).invalid,
- },
- )}
- key={field.id}
- >
-
-
-
+ {idx > 0 && (
+
+ )}
))}
diff --git a/web/src/pages/docker-compose/docker-compose.tsx b/web/src/pages/docker-compose/docker-compose.tsx
index b5bbabc9..1c22b224 100644
--- a/web/src/pages/docker-compose/docker-compose.tsx
+++ b/web/src/pages/docker-compose/docker-compose.tsx
@@ -12,6 +12,7 @@ import {
INetworkConfig,
TDockerCompose,
} from './docker-compose.type';
+import type { DockerCompose } from './docker-compose.type';
import { cn } from '@/lib/utils';
import ServiceNetworkFields from './components/service-network-fields';
import ServiceDependsOnFields from './components/service-depends-on-fields';
@@ -50,24 +51,46 @@ const DockerCompose: FC = () => {
build: {
context: '',
dockerfile: '',
- args: [],
+ args: [
+ {
+ key: '',
+ value: '',
+ },
+ ],
},
- command: '',
- container_name: '',
- environment: [],
+ command: null,
+ container_name: null,
+ environment: [
+ {
+ key: '',
+ value: '',
+ },
+ ],
image: '',
- ports: [''],
- volumes: [''],
- networks: [''],
- depends_on: [''],
+ ports: [
+ {
+ value: '',
+ },
+ ],
+ volumes: [
+ {
+ value: '',
+ },
+ ],
+ networks: [
+ {
+ value: '',
+ },
+ ],
+ depends_on: [{ value: '' }],
},
],
networks: {
- custom: false,
+ external_network: false,
app_network: [
{
network_name: '',
- driver: { value: 'bridge', label: 'bridge' },
+ driver: { value: 'bridge', label: 'Bridge' },
},
],
},
@@ -92,19 +115,45 @@ const DockerCompose: FC = () => {
const handleAddService = () => {
append({
build: {
- args: [],
+ args: [
+ {
+ key: '',
+ value: '',
+ },
+ ],
context: '',
dockerfile: '',
},
name: '',
- command: '',
- container_name: '',
+ command: null,
+ container_name: null,
image: '',
- environment: [],
- depends_on: [''],
- networks: [''],
- ports: [''],
- volumes: [''],
+ environment: [
+ {
+ key: '',
+ value: '',
+ },
+ ],
+ depends_on: [
+ {
+ value: '',
+ },
+ ],
+ networks: [
+ {
+ value: '',
+ },
+ ],
+ ports: [
+ {
+ value: '',
+ },
+ ],
+ volumes: [
+ {
+ value: '',
+ },
+ ],
});
};
@@ -131,49 +180,58 @@ const DockerCompose: FC = () => {
}),
);
- const refactoredNetwork = data.networks.app_network.reduce(
- (acc: INetworkConfig, network) => {
- if (!data.networks.custom) {
- if ('driver' in network) {
+ let refactoredNetwork: any;
+
+ if (!data.networks.app_network.some((network) => network.network_name)) {
+ refactoredNetwork = null;
+ } else {
+ refactoredNetwork = data.networks.app_network.reduce(
+ (acc: INetworkConfig, network) => {
+ if (!data.networks.external_network) {
+ if ('driver' in network) {
+ acc[network.network_name] = {
+ driver: network.driver?.value,
+ };
+ }
+ }
+ if ('name' in network) {
acc[network.network_name] = {
- driver: network.driver?.value,
+ name: network.name,
+ external_network: true,
};
}
- }
- if ('name' in network && 'external' in network) {
- acc[network.network_name] = {
- name: network.name,
- external: !!network.external,
- };
- }
- return acc;
- },
- {},
- );
+ return acc;
+ },
+ {},
+ );
+ }
const services = refactoredService.map((item) => {
- if (item.ports && item?.ports[0].length === 0) {
- item.ports = null;
- }
- if (item.volumes && item.volumes[0].length === 0) {
- item.volumes = null;
- }
- if (item.networks && item.networks[0].length === 0) {
- item.networks = null;
- }
- if (item.depends_on && item.depends_on[0].length === 0) {
- item.depends_on = null;
- }
- if (item.environment && !item.environment[0]) {
- item.environment = null;
- }
- if (item.environment && item.environment[0]) {
- item.environment = null;
- }
- if (item.command?.length === 0) {
- item.command = null;
- }
- return item;
+ const bodyService = {
+ name: item.name,
+ build: item.build?.context ? item.build : null,
+ command: item.command ? item.command : null,
+ image: item.image ? item.image : null,
+ container_name: item.container_name ? item.container_name : null,
+ depends_on: item.depends_on?.some((item) => item.value) ? [''] : null,
+ ports: item.ports?.some((item) => item.value)
+ ? item.ports.map((port) => port.value)
+ : null,
+ volumes: item.volumes?.some((item) => item.value)
+ ? item.volumes.map((volume) => volume.value)
+ : null,
+ networks: item.networks?.some((item) => item.value)
+ ? item.networks.map((network) => network.value)
+ : null,
+ environment:
+ item.environment &&
+ Object.entries(item.environment).some(
+ ([k, v]) => k !== '' || v !== '',
+ )
+ ? item.environment
+ : null,
+ };
+ return bodyService;
});
const requestBody: DockerComposeBody = {
@@ -185,13 +243,11 @@ const DockerCompose: FC = () => {
await dockerComposeMutate(requestBody);
await download();
} catch (error) {
- console.log(error);
if (isAxiosError
(error)) {
toast.error(
`${error.response?.data.detail[0].loc[error.response?.data.detail[0].loc.length - 1]} ${error.response?.data.detail[0].msg}`,
);
} else {
- console.log(error);
toast.error('Something went wrong');
}
}
@@ -255,7 +311,7 @@ const DockerCompose: FC = () => {
{
id="command"
name={`services.${index}.command`}
label="Command"
+ placeholder="command..."
/>
diff --git a/web/src/pages/docker-compose/docker-compose.type.ts b/web/src/pages/docker-compose/docker-compose.type.ts
index 3b36bcfb..f8307d64 100644
--- a/web/src/pages/docker-compose/docker-compose.type.ts
+++ b/web/src/pages/docker-compose/docker-compose.type.ts
@@ -31,7 +31,7 @@ export interface INetworkConfig {
}
| {
name: string;
- external: boolean;
+ external_network: boolean;
};
}
@@ -57,37 +57,58 @@ export interface DockerComposeValidationError {
}
const KV_Schema = zod.array(
- zod
- .object({
- key: zod
- .string()
- .min(1, { message: 'Key must be at least 1 character long' }),
- value: zod
- .string()
- .min(1, { message: 'Value must be at least 1 character long' }),
- })
- .nullable(),
+ zod.object({
+ key: zod.string(),
+ value: zod.string(),
+ }),
);
export const BuildSchema = zod.object({
enabled: zod.boolean(),
args: KV_Schema,
- context: zod.string(),
- dockerfile: zod.string(),
+ context: zod.string().optional(),
+ dockerfile: zod.string().optional(),
});
-export const ServiceSchema = zod.object({
- name: zod.string(),
- build: BuildSchema,
- image: zod.string(),
- environment: KV_Schema,
- container_name: zod.string(),
- ports: zod.array(zod.string()).nullable(),
- command: zod.string().optional().nullable(),
- volumes: zod.array(zod.string()).nullable(),
- networks: zod.array(zod.string()).nullable(),
- depends_on: zod.array(zod.string()).nullable(),
-});
+export const ServiceSchema = zod
+ .object({
+ name: zod.string().min(1, 'Name is required!'),
+ build: BuildSchema,
+ image: zod.string().nullable(),
+ environment: KV_Schema,
+ container_name: zod.string().nullable(),
+ ports: zod.array(zod.object({ value: zod.string() })).nullable(),
+ command: zod.string().optional().nullable(),
+ volumes: zod.array(zod.object({ value: zod.string() })).nullable(),
+ networks: zod.array(zod.object({ value: zod.string() })).nullable(),
+ depends_on: zod.array(zod.object({ value: zod.string() })).nullable(),
+ })
+ .superRefine((data, ctx) => {
+ if (!data.build.enabled && !data.image) {
+ ctx.addIssue({
+ path: ['image'],
+ message: 'Image is required.',
+ code: zod.ZodIssueCode.custom,
+ });
+ }
+
+ if (data.build.enabled) {
+ if (!data.build.context) {
+ ctx.addIssue({
+ path: ['build', 'context'],
+ message: 'Context is required.',
+ code: zod.ZodIssueCode.custom,
+ });
+ }
+ if (!data.build.dockerfile) {
+ ctx.addIssue({
+ path: ['build', 'dockerfile'],
+ message: 'Dockerfile is required.',
+ code: zod.ZodIssueCode.custom,
+ });
+ }
+ }
+ });
const labelValueSchema = zod.object({
label: zod.string(),
@@ -96,7 +117,7 @@ const labelValueSchema = zod.object({
export const NetworkSchema = zod.union([
zod.object({
- custom: zod.literal(false),
+ external_network: zod.literal(false),
app_network: zod.array(
zod.object({
network_name: zod.string(),
@@ -105,25 +126,44 @@ export const NetworkSchema = zod.union([
),
}),
zod.object({
- custom: zod.literal(true),
+ external_network: zod.literal(true),
app_network: zod.array(
zod.object({
- network_name: zod.string(),
- external: zod.boolean().optional(),
- name: zod.string(),
+ network_name: zod.string().min(1, 'Network name is required.'),
+ name: zod.string().min(1, 'Name is required.'),
}),
),
}),
]);
export const DockerComposeSchema = zod.object({
- version: zod.string(),
+ version: zod.string().min(1, 'Version is required.'),
services: zod.array(ServiceSchema),
networks: NetworkSchema,
});
export type TDockerCompose = zod.infer
;
+export type DockerCompose = {
+ version: string;
+ services: {
+ [key: string]: {
+ build: IBuildConfig | null;
+ image: string | null;
+ environment: {
+ [key: string]: string;
+ } | null;
+ container_name: string | null;
+ ports: string[] | null;
+ command: string | null;
+ volumes: string[] | null;
+ networks: string[] | null;
+ depends_on: string[] | null;
+ };
+ }[];
+ networks: INetworkConfig | null;
+};
+
type AppNetwork = {
network_name: string;
driver: {
diff --git a/web/src/pages/helm-template/components/pod-environment-fields.tsx b/web/src/pages/helm-template/components/pod-environment-fields.tsx
index 2a1192f1..bd4dfed4 100644
--- a/web/src/pages/helm-template/components/pod-environment-fields.tsx
+++ b/web/src/pages/helm-template/components/pod-environment-fields.tsx
@@ -16,14 +16,14 @@ const PodEnvironmentFields: FC = ({ podIndex }) => {
});
return (
-
-
+
+
Environments
@@ -32,11 +32,14 @@ const PodEnvironmentFields: FC
= ({ podIndex }) => {
{fields.map((field, envIdx) => (
div]:mb-0',
+ 'relative mb-4 flex items-center divide-x-2 divide-gray-200 rounded-md border border-gray-200 dark:divide-gray-500 dark:border-gray-500 [&>div]:mb-0',
{
'divide-red-500 border-red-500 dark:divide-red-500 dark:border-red-500':
control.getFieldState(
`pods.${podIndex}.environment.${envIdx}.name`,
+ ).invalid ||
+ control.getFieldState(
+ `pods.${podIndex}.environment.${envIdx}.value`,
).invalid,
},
)}
@@ -47,23 +50,22 @@ const PodEnvironmentFields: FC
= ({ podIndex }) => {
name={`pods.${podIndex}.environment.${envIdx}.name`}
label=""
placeholder="Env"
- className="h-12 w-full rounded-s-md px-2 outline-none"
+ inputClass={'border-none'}
/>
{envIdx > 0 && (
)}
diff --git a/web/src/pages/helm-template/helm-template.tsx b/web/src/pages/helm-template/helm-template.tsx
index 4ff61952..a7122867 100644
--- a/web/src/pages/helm-template/helm-template.tsx
+++ b/web/src/pages/helm-template/helm-template.tsx
@@ -119,7 +119,7 @@ const HelmTemplate: FC = () => {
-
+
{
isNumber={true}
/>
-
+
Pods
@@ -144,7 +144,7 @@ const HelmTemplate: FC = () => {
{pods.map((pod, index) => (
{
-
+
-
-
-
-
{
inputType="number"
isNumber={true}
/>
-
-
{
isNumber={true}
/>
-
Persistence
-
+
Persistence
+
-
+
{
-
+
@@ -254,8 +248,8 @@ const HelmTemplate: FC = () => {
label=""
/>
-
Ingress
-
+
Ingress
+
@@ -265,7 +259,7 @@ const HelmTemplate: FC = () => {
label=""
/>
-
+
{