mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-10-19 07:01:35 +00:00
63efbffcfd
Network sync compared only by name, so a network deleted and recreated with the same name but different attributes (driver, attachable, etc.) was reported as already in sync. Store Docker's network Id (dockerId column) and compare it against the live Id during sync. A name match with a dockerId mismatch is now surfaced as a new "changed" state, with an Update action to refresh the DB row from the live network. Fixes #5179
137 lines
3.4 KiB
TypeScript
137 lines
3.4 KiB
TypeScript
import { relations } from "drizzle-orm";
|
|
import {
|
|
boolean,
|
|
integer,
|
|
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";
|
|
|
|
export const networkDriver = pgEnum("networkDriver", ["bridge", "overlay"]);
|
|
|
|
export const network = pgTable("network", {
|
|
networkId: text("networkId")
|
|
.notNull()
|
|
.primaryKey()
|
|
.$defaultFn(() => nanoid()),
|
|
name: text("name").notNull(),
|
|
dockerId: text("dockerId"),
|
|
driver: networkDriver("driver").notNull().default("bridge"),
|
|
internal: boolean("internal").notNull().default(false),
|
|
attachable: boolean("attachable").notNull().default(false),
|
|
enableIPv4: boolean("enableIPv4").notNull().default(true),
|
|
enableIPv6: boolean("enableIPv6").notNull().default(false),
|
|
mtu: integer("mtu"),
|
|
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", "overlay"]).optional(),
|
|
internal: z.boolean().optional(),
|
|
attachable: z.boolean().optional(),
|
|
enableIPv4: z.boolean().optional(),
|
|
enableIPv6: z.boolean().optional(),
|
|
mtu: z.number().int().min(68).max(65535).optional().nullable(),
|
|
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(),
|
|
});
|
|
|
|
const validateNetworkInput = (
|
|
input: {
|
|
enableIPv4?: boolean;
|
|
enableIPv6?: boolean;
|
|
ipam?: {
|
|
config?: Array<{ subnet?: string; gateway?: string; ipRange?: string }>;
|
|
} | null;
|
|
},
|
|
ctx: z.RefinementCtx,
|
|
) => {
|
|
if (input.enableIPv4 === false && input.enableIPv6 !== true) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
path: ["enableIPv4"],
|
|
message: "IPv4 or IPv6 must be enabled",
|
|
});
|
|
}
|
|
for (const [index, entry] of (input.ipam?.config ?? []).entries()) {
|
|
if (!entry.subnet && (entry.gateway || entry.ipRange)) {
|
|
ctx.addIssue({
|
|
code: z.ZodIssueCode.custom,
|
|
path: ["ipam", "config", index, "subnet"],
|
|
message: "Gateway and IP range require a subnet",
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
export const apiCreateNetwork = createSchema
|
|
.pick({
|
|
name: true,
|
|
driver: true,
|
|
internal: true,
|
|
attachable: true,
|
|
enableIPv4: true,
|
|
enableIPv6: true,
|
|
mtu: true,
|
|
ipam: true,
|
|
serverId: true,
|
|
})
|
|
.partial()
|
|
.required({ name: true })
|
|
.superRefine(validateNetworkInput);
|
|
|
|
export const apiFindOneNetwork = z.object({
|
|
networkId: z.string().min(1),
|
|
});
|
|
|
|
export const apiRemoveNetwork = z.object({
|
|
networkId: z.string().min(1),
|
|
});
|