feat: implement Docker network management functionality

- Added components for handling and displaying Docker networks, including creation, editing, and listing of networks.
- Introduced a new API router for network operations, integrating with the database schema for network management.
- Updated the sidebar layout to include a link to the networks dashboard, ensuring user access to network features.
- Created necessary database migrations for the network table and its associated types.
- Enhanced the dashboard layout to support the new network management interface.
This commit is contained in:
Mauricio Siu
2026-02-21 14:53:27 -06:00
parent 3e461f642e
commit 69598821ed
15 changed files with 8742 additions and 0 deletions
+2
View File
@@ -7,6 +7,7 @@ import {
timestamp,
} from "drizzle-orm/pg-core";
import { nanoid } from "nanoid";
import { network } from "./network";
import { projects } from "./project";
import { server } from "./server";
import { ssoProvider } from "./sso";
@@ -77,6 +78,7 @@ export const organizationRelations = relations(
references: [user.id],
}),
servers: many(server),
networks: many(network),
projects: many(projects),
members: many(member),
ssoProviders: many(ssoProvider),
+1
View File
@@ -16,6 +16,7 @@ export * from "./gitlab";
export * from "./mariadb";
export * from "./mongo";
export * from "./mount";
export * from "./network";
export * from "./mysql";
export * from "./notification";
export * from "./patch";
+137
View File
@@ -0,0 +1,137 @@
import { relations } from "drizzle-orm";
import { boolean, jsonb, pgEnum, pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { organization } from "./account";
import { server } from "./server";
/** Docker network driver types */
export const networkDriver = pgEnum("networkDriver", [
"bridge",
"host",
"overlay",
"macvlan",
"none",
"ipvlan",
]);
export const network = pgTable("network", {
networkId: text("networkId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
driver: networkDriver("driver").notNull().default("bridge"),
scope: text("scope"), // e.g. "local", "swarm"
internal: boolean("internal").notNull().default(false),
attachable: boolean("attachable").notNull().default(false),
ingress: boolean("ingress").notNull().default(false),
configOnly: boolean("configOnly").notNull().default(false),
enableIPv4: boolean("enableIPv4").notNull().default(true),
enableIPv6: boolean("enableIPv6").notNull().default(false),
ipam: jsonb("ipam")
.$type<{
driver?: string;
config?: Array<{ subnet?: string; gateway?: string; ipRange?: string }>;
}>()
.default({}),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
organizationId: text("organizationId")
.notNull()
.references(() => organization.id, { onDelete: "cascade" }),
serverId: text("serverId").references(() => server.serverId, {
onDelete: "cascade",
}),
});
export const networkRelations = relations(network, ({ one }) => ({
organization: one(organization, {
fields: [network.organizationId],
references: [organization.id],
}),
server: one(server, {
fields: [network.serverId],
references: [server.serverId],
}),
}));
const createSchema = createInsertSchema(network, {
networkId: z.string().min(1),
name: z.string().min(1),
driver: z
.enum(["bridge", "host", "overlay", "macvlan", "none", "ipvlan"])
.optional(),
scope: z.string().optional(),
internal: z.boolean().optional(),
attachable: z.boolean().optional(),
ingress: z.boolean().optional(),
configOnly: z.boolean().optional(),
enableIPv4: z.boolean().optional(),
enableIPv6: z.boolean().optional(),
ipam: z
.object({
driver: z.string().optional(),
config: z
.array(
z.object({
subnet: z.string().optional(),
gateway: z.string().optional(),
ipRange: z.string().optional(),
}),
)
.optional(),
})
.optional(),
organizationId: z.string().min(1),
serverId: z.string().optional().nullable(),
});
export const apiCreateNetwork = createSchema
.pick({
name: true,
driver: true,
scope: true,
internal: true,
attachable: true,
ingress: true,
configOnly: true,
enableIPv4: true,
enableIPv6: true,
ipam: true,
serverId: true,
})
.partial()
.required({ name: true });
export const apiFindOneNetwork = createSchema
.pick({
networkId: true,
})
.required();
export const apiRemoveNetwork = createSchema
.pick({
networkId: true,
})
.required();
export const apiUpdateNetwork = createSchema
.pick({
networkId: true,
name: true,
driver: true,
scope: true,
internal: true,
attachable: true,
ingress: true,
configOnly: true,
enableIPv4: true,
enableIPv6: true,
ipam: true,
serverId: true,
})
.partial()
.required({ networkId: true });
+2
View File
@@ -18,6 +18,7 @@ import { deployments } from "./deployment";
import { mariadb } from "./mariadb";
import { mongo } from "./mongo";
import { mysql } from "./mysql";
import { network } from "./network";
import { postgres } from "./postgres";
import { redis } from "./redis";
import { schedules } from "./schedule";
@@ -122,6 +123,7 @@ export const serverRelations = relations(server, ({ one, many }) => ({
mysql: many(mysql),
postgres: many(postgres),
certificates: many(certificates),
networks: many(network),
organization: one(organization, {
fields: [server.organizationId],
references: [organization.id],