feat(networks): enhance network management and UI components

- Added functionality for assigning Docker networks to services, allowing users to manage network associations directly from the UI.
- Introduced new components for assigning networks to both individual services and compose files, improving user experience and flexibility.
- Updated existing components to integrate network assignment features, ensuring seamless interaction within the dashboard.
- Enhanced the database schema to support new network-related fields, including `networkIds` and `detachDokployNetwork`, for better service configuration.
- Improved the layout and responsiveness of various UI elements to accommodate new features and enhance usability.
This commit is contained in:
Mauricio Siu
2026-07-25 03:09:54 -06:00
parent 8a37626636
commit 97885ee44a
57 changed files with 1382 additions and 307 deletions
+11 -2
View File
@@ -83,8 +83,17 @@ export const findDeploymentById = async (deploymentId: string) => {
const deployment = await db.query.deployments.findFirst({
where: eq(deployments.deploymentId, deploymentId),
with: {
application: true,
compose: true,
application: {
columns: {
applicationId: true,
appName: true,
name: true,
serverId: true,
},
},
compose: {
columns: { composeId: true, appName: true, name: true, serverId: true },
},
schedule: true,
},
});
+9 -3
View File
@@ -80,7 +80,9 @@ export const findDomainById = async (domainId: string) => {
const domain = await db.query.domains.findFirst({
where: eq(domains.domainId, domainId),
with: {
application: true,
application: {
columns: { applicationId: true, appName: true, name: true },
},
},
});
if (!domain) {
@@ -96,7 +98,9 @@ export const findDomainsByApplicationId = async (applicationId: string) => {
const domainsArray = await db.query.domains.findMany({
where: eq(domains.applicationId, applicationId),
with: {
application: true,
application: {
columns: { applicationId: true, appName: true, name: true },
},
},
});
@@ -107,7 +111,9 @@ export const findDomainsByComposeId = async (composeId: string) => {
const domainsArray = await db.query.domains.findMany({
where: eq(domains.composeId, composeId),
with: {
compose: true,
compose: {
columns: { composeId: true, appName: true, name: true },
},
},
});
+41 -8
View File
@@ -201,18 +201,51 @@ export const findEnvironmentById = async (environmentId: string) => {
};
export const findEnvironmentsByProjectId = async (projectId: string) => {
const serviceColumns = {
name: true,
description: true,
appName: true,
createdAt: true,
serverId: true,
applicationStatus: true,
} as const;
const projectEnvironments = await db.query.environments.findMany({
where: eq(environments.projectId, projectId),
orderBy: asc(environments.createdAt),
with: {
applications: true,
mariadb: true,
mongo: true,
mysql: true,
postgres: true,
redis: true,
compose: true,
libsql: true,
applications: {
columns: { ...serviceColumns, applicationId: true, icon: true },
with: { server: { columns: { name: true } } },
},
mariadb: {
columns: { ...serviceColumns, mariadbId: true },
with: { server: { columns: { name: true } } },
},
mongo: {
columns: { ...serviceColumns, mongoId: true },
with: { server: { columns: { name: true } } },
},
mysql: {
columns: { ...serviceColumns, mysqlId: true },
with: { server: { columns: { name: true } } },
},
postgres: {
columns: { ...serviceColumns, postgresId: true },
with: { server: { columns: { name: true } } },
},
redis: {
columns: { ...serviceColumns, redisId: true },
with: { server: { columns: { name: true } } },
},
compose: {
columns: { ...serviceColumns, composeId: true, composeStatus: true },
with: { server: { columns: { name: true } } },
},
libsql: {
columns: { ...serviceColumns, libsqlId: true },
with: { server: { columns: { name: true } } },
},
project: true,
},
columns: {
+20 -72
View File
@@ -107,81 +107,29 @@ export const createFileMount = async (mountId: string) => {
};
export const findMountById = async (mountId: string) => {
const serviceWith = {
columns: { serverId: true, appName: true },
with: {
environment: {
columns: {},
with: {
project: { columns: { organizationId: true } },
},
},
},
} as const;
const mount = await db.query.mounts.findFirst({
where: eq(mounts.mountId, mountId),
with: {
application: {
with: {
environment: {
with: {
project: true,
},
},
},
},
compose: {
with: {
environment: {
with: {
project: true,
},
},
},
},
libsql: {
with: {
environment: {
with: {
project: true,
},
},
},
},
mariadb: {
with: {
environment: {
with: {
project: true,
},
},
},
},
mongo: {
with: {
environment: {
with: {
project: true,
},
},
},
},
mysql: {
with: {
environment: {
with: {
project: true,
},
},
},
},
postgres: {
with: {
environment: {
with: {
project: true,
},
},
},
},
redis: {
with: {
environment: {
with: {
project: true,
},
},
},
},
application: serviceWith,
compose: serviceWith,
libsql: serviceWith,
mariadb: serviceWith,
mongo: serviceWith,
mysql: serviceWith,
postgres: serviceWith,
redis: serviceWith,
},
});
if (!mount) {
+29 -3
View File
@@ -1,9 +1,10 @@
import { db } from "@dokploy/server/db";
import { type apiCreateNetwork, network } from "@dokploy/server/db/schema";
import { TRPCError } from "@trpc/server";
import { and, eq, isNull } from "drizzle-orm";
import { and, eq, inArray, isNull } from "drizzle-orm";
import type { z } from "zod";
import { IS_CLOUD } from "../constants";
import type { ApplicationNested } from "../utils/builders";
import { getRemoteDocker } from "../utils/servers/remote-docker";
// Networks managed by Docker/Dokploy itself that must never be imported
@@ -273,14 +274,39 @@ const createDockerNetworkFromRow = async (row: typeof network.$inferSelect) => {
}
};
// Re-creates the Docker network from the stored record, for records whose
// network was removed from Docker outside of Dokploy
export const recreateNetwork = async (networkId: string) => {
const row = await findNetworkById(networkId);
await createDockerNetworkFromRow(row);
return row;
};
export const resolveServiceNetworks = async (
application: Partial<ApplicationNested>,
) => {
if (application.networkSwarm) {
return application.networkSwarm;
}
const { networkIds, detachDokployNetwork } = application;
const rows =
networkIds && networkIds.length > 0
? await db.query.network.findMany({
where: and(
inArray(network.networkId, networkIds),
eq(network.driver, "overlay"),
),
columns: { name: true },
})
: [];
const networks = detachDokployNetwork ? [] : ["dokploy-network"];
for (const row of rows) {
networks.push(row.name);
}
return networks.map((name) => ({ Target: name }));
};
export const inspectNetwork = async (networkId: string) => {
const row = await findNetworkById(networkId);
+71 -8
View File
@@ -48,19 +48,60 @@ export const createProject = async (
};
export const findProjectById = async (projectId: string) => {
const serviceColumns = {
name: true,
description: true,
appName: true,
createdAt: true,
serverId: true,
applicationStatus: true,
} as const;
const project = await db.query.projects.findFirst({
where: eq(projects.projectId, projectId),
with: {
environments: {
with: {
applications: true,
compose: true,
libsql: true,
mariadb: true,
mongo: true,
mysql: true,
postgres: true,
redis: true,
applications: {
columns: {
...serviceColumns,
applicationId: true,
icon: true,
},
with: { server: { columns: { name: true } } },
},
compose: {
columns: {
...serviceColumns,
composeId: true,
composeStatus: true,
},
with: { server: { columns: { name: true } } },
},
libsql: {
columns: { ...serviceColumns, libsqlId: true },
with: { server: { columns: { name: true } } },
},
mariadb: {
columns: { ...serviceColumns, mariadbId: true },
with: { server: { columns: { name: true } } },
},
mongo: {
columns: { ...serviceColumns, mongoId: true },
with: { server: { columns: { name: true } } },
},
mysql: {
columns: { ...serviceColumns, mysqlId: true },
with: { server: { columns: { name: true } } },
},
postgres: {
columns: { ...serviceColumns, postgresId: true },
with: { server: { columns: { name: true } } },
},
redis: {
columns: { ...serviceColumns, redisId: true },
with: { server: { columns: { name: true } } },
},
},
},
projectTags: {
@@ -107,27 +148,49 @@ export const updateProjectById = async (
export const validUniqueServerAppName = async (appName: string) => {
const query = await db.query.environments.findMany({
columns: { environmentId: true },
with: {
applications: {
where: eq(applications.appName, appName),
columns: {
appName: true,
},
},
libsql: {
where: eq(libsql.appName, appName),
columns: {
appName: true,
},
},
mariadb: {
where: eq(mariadb.appName, appName),
columns: {
appName: true,
},
},
mongo: {
where: eq(mongo.appName, appName),
columns: {
appName: true,
},
},
mysql: {
where: eq(mysql.appName, appName),
columns: {
appName: true,
},
},
postgres: {
where: eq(postgres.appName, appName),
columns: {
appName: true,
},
},
redis: {
where: eq(redis.appName, appName),
columns: {
appName: true,
},
},
},
});
+6 -2
View File
@@ -20,6 +20,7 @@ import { getRemoteDocker } from "../utils/servers/remote-docker";
import { type Application, findApplicationById } from "./application";
import { findDeploymentById } from "./deployment";
import type { Mount } from "./mount";
import { resolveServiceNetworks } from "./network";
import type { Port } from "./port";
import type { Project } from "./project";
import {
@@ -257,6 +258,10 @@ const rollbackApplication = async (
const volumesMount = generateVolumeMounts(mounts);
const resolvedNetworks = await resolveServiceNetworks(
fullContext as Parameters<typeof resolveServiceNetworks>[0],
);
const {
HealthCheck,
RestartPolicy,
@@ -265,7 +270,6 @@ const rollbackApplication = async (
Mode,
RollbackConfig,
UpdateConfig,
Networks,
Ulimits,
} = generateConfigContainer(
fullContext as Parameters<typeof generateConfigContainer>[0],
@@ -304,7 +308,7 @@ const rollbackApplication = async (
...(Ulimits && { Ulimits }),
Labels,
},
Networks,
Networks: resolvedNetworks,
RestartPolicy,
Placement,
Resources: {
+9 -8
View File
@@ -100,15 +100,16 @@ export const deleteServer = async (serverId: string) => {
export const haveActiveServices = async (serverId: string) => {
const currentServer = await db.query.server.findFirst({
where: eq(server.serverId, serverId),
columns: { serverId: true },
with: {
applications: true,
compose: true,
libsql: true,
mariadb: true,
mongo: true,
mysql: true,
postgres: true,
redis: true,
applications: { columns: { applicationId: true } },
compose: { columns: { composeId: true } },
libsql: { columns: { libsqlId: true } },
mariadb: { columns: { mariadbId: true } },
mongo: { columns: { mongoId: true } },
mysql: { columns: { mysqlId: true } },
postgres: { columns: { postgresId: true } },
redis: { columns: { redisId: true } },
},
});