mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
v0.1.0 (#112)
* feat: add schema for registry and routes * feat: add docker registry upload * feat: add show cluster * refactor: set the registry url in image in case we have a registry asociated * feat: add update registry and fix the docker url markup * chore: remove --advertise-ip on swarm script * refactor: remove listen address of swarm initialize * feat: add table to show nodes and add dropdown to add manager & workers * refactor: improve interface for cluster * refactor: improve UI * feat: add experimental swarm settings * refactor: remove comments * refactor: prettify json of each setting * refactor: add interface tooltip * refactor: delete static form self registry * refactor: allow to se a empty registry * fix: remove text area warnings * feat: add network swarm json * refactor: update ui * revert: go back to swarm init config * refactor: remove initialization on server, only on setup script * Update LICENSE.MD * feat: appearance theme support system config * refactor: remove logs * fix(README-ru): hyperlink-ed docs url * feat: (#107) webhook listener filter docker events based on image tag. Fixes #107 * refactor: simplify comparison docker tags * refactor: remove return in res status * refactor: prevent to updates download automatically * feat: support code editor (#105) * feat: support code editor * Update codeblock * refactor: remove unused class --------- Co-authored-by: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> * fix: select the right image from sourcetype (#109) * chore: bump minor version --------- Co-authored-by: hehehai <riverhohai@gmail.com> Co-authored-by: Bayram Tagiev <bayram.tagiev.a@gmail.com> Co-authored-by: Paulo Santana <30875229+hikinine@users.noreply.github.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import { users } from "./user";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
import { certificateType } from "./shared";
|
||||
import { registry } from "./registry";
|
||||
|
||||
export const admins = pgTable("admin", {
|
||||
adminId: text("adminId")
|
||||
@@ -39,6 +40,7 @@ export const adminsRelations = relations(admins, ({ one, many }) => ({
|
||||
references: [auth.id],
|
||||
}),
|
||||
users: many(users),
|
||||
registry: many(registry),
|
||||
}));
|
||||
|
||||
const createSchema = createInsertSchema(admins, {
|
||||
|
||||
@@ -10,8 +10,16 @@ import { projects } from "./project";
|
||||
import { security } from "./security";
|
||||
import { applicationStatus } from "./shared";
|
||||
import { ports } from "./port";
|
||||
import { boolean, integer, pgEnum, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import {
|
||||
boolean,
|
||||
integer,
|
||||
json,
|
||||
pgEnum,
|
||||
pgTable,
|
||||
text,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { generateAppName } from "./utils";
|
||||
import { registry } from "./registry";
|
||||
|
||||
export const sourceType = pgEnum("sourceType", ["docker", "git", "github"]);
|
||||
|
||||
@@ -22,6 +30,65 @@ export const buildType = pgEnum("buildType", [
|
||||
"nixpacks",
|
||||
]);
|
||||
|
||||
// TODO: refactor this types
|
||||
interface HealthCheckSwarm {
|
||||
Test?: string[] | undefined;
|
||||
Interval?: number | undefined;
|
||||
Timeout?: number | undefined;
|
||||
StartPeriod?: number | undefined;
|
||||
Retries?: number | undefined;
|
||||
}
|
||||
|
||||
interface RestartPolicySwarm {
|
||||
Condition?: string | undefined;
|
||||
Delay?: number | undefined;
|
||||
MaxAttempts?: number | undefined;
|
||||
Window?: number | undefined;
|
||||
}
|
||||
|
||||
interface PlacementSwarm {
|
||||
Constraints?: string[] | undefined;
|
||||
Preferences?: Array<{ Spread: { SpreadDescriptor: string } }> | undefined;
|
||||
MaxReplicas?: number | undefined;
|
||||
Platforms?:
|
||||
| Array<{
|
||||
Architecture: string;
|
||||
OS: string;
|
||||
}>
|
||||
| undefined;
|
||||
}
|
||||
|
||||
interface UpdateConfigSwarm {
|
||||
Parallelism: number;
|
||||
Delay?: number | undefined;
|
||||
FailureAction?: string | undefined;
|
||||
Monitor?: number | undefined;
|
||||
MaxFailureRatio?: number | undefined;
|
||||
Order: string;
|
||||
}
|
||||
|
||||
interface ServiceModeSwarm {
|
||||
Replicated?: { Replicas?: number | undefined } | undefined;
|
||||
Global?: {} | undefined;
|
||||
ReplicatedJob?:
|
||||
| {
|
||||
MaxConcurrent?: number | undefined;
|
||||
TotalCompletions?: number | undefined;
|
||||
}
|
||||
| undefined;
|
||||
GlobalJob?: {} | undefined;
|
||||
}
|
||||
|
||||
interface NetworkSwarm {
|
||||
Target?: string | undefined;
|
||||
Aliases?: string[] | undefined;
|
||||
DriverOpts?: { [key: string]: string } | undefined;
|
||||
}
|
||||
|
||||
interface LabelsSwarm {
|
||||
[name: string]: string;
|
||||
}
|
||||
|
||||
export const applications = pgTable("application", {
|
||||
applicationId: text("applicationId")
|
||||
.notNull()
|
||||
@@ -60,6 +127,17 @@ export const applications = pgTable("application", {
|
||||
customGitBuildPath: text("customGitBuildPath"),
|
||||
customGitSSHKey: text("customGitSSHKey"),
|
||||
dockerfile: text("dockerfile"),
|
||||
// Docker swarm json
|
||||
healthCheckSwarm: json("healthCheckSwarm").$type<HealthCheckSwarm>(),
|
||||
restartPolicySwarm: json("restartPolicySwarm").$type<RestartPolicySwarm>(),
|
||||
placementSwarm: json("placementSwarm").$type<PlacementSwarm>(),
|
||||
updateConfigSwarm: json("updateConfigSwarm").$type<UpdateConfigSwarm>(),
|
||||
rollbackConfigSwarm: json("rollbackConfigSwarm").$type<UpdateConfigSwarm>(),
|
||||
modeSwarm: json("modeSwarm").$type<ServiceModeSwarm>(),
|
||||
labelsSwarm: json("labelsSwarm").$type<LabelsSwarm>(),
|
||||
networkSwarm: json("networkSwarm").$type<NetworkSwarm[]>(),
|
||||
//
|
||||
replicas: integer("replicas").default(1).notNull(),
|
||||
applicationStatus: applicationStatus("applicationStatus")
|
||||
.notNull()
|
||||
.default("idle"),
|
||||
@@ -67,6 +145,9 @@ export const applications = pgTable("application", {
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
registryId: text("registryId").references(() => registry.registryId, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
projectId: text("projectId")
|
||||
.notNull()
|
||||
.references(() => projects.projectId, { onDelete: "cascade" }),
|
||||
@@ -85,9 +166,101 @@ export const applicationsRelations = relations(
|
||||
redirects: many(redirects),
|
||||
security: many(security),
|
||||
ports: many(ports),
|
||||
registry: one(registry, {
|
||||
fields: [applications.registryId],
|
||||
references: [registry.registryId],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
const HealthCheckSwarmSchema = z
|
||||
.object({
|
||||
Test: z.array(z.string()).optional(),
|
||||
Interval: z.number().optional(),
|
||||
Timeout: z.number().optional(),
|
||||
StartPeriod: z.number().optional(),
|
||||
Retries: z.number().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const RestartPolicySwarmSchema = z
|
||||
.object({
|
||||
Condition: z.string().optional(),
|
||||
Delay: z.number().optional(),
|
||||
MaxAttempts: z.number().optional(),
|
||||
Window: z.number().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const PreferenceSchema = z
|
||||
.object({
|
||||
Spread: z.object({
|
||||
SpreadDescriptor: z.string(),
|
||||
}),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const PlatformSchema = z
|
||||
.object({
|
||||
Architecture: z.string(),
|
||||
OS: z.string(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const PlacementSwarmSchema = z
|
||||
.object({
|
||||
Constraints: z.array(z.string()).optional(),
|
||||
Preferences: z.array(PreferenceSchema).optional(),
|
||||
MaxReplicas: z.number().optional(),
|
||||
Platforms: z.array(PlatformSchema).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const UpdateConfigSwarmSchema = z
|
||||
.object({
|
||||
Parallelism: z.number(),
|
||||
Delay: z.number().optional(),
|
||||
FailureAction: z.string().optional(),
|
||||
Monitor: z.number().optional(),
|
||||
MaxFailureRatio: z.number().optional(),
|
||||
Order: z.string(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const ReplicatedSchema = z
|
||||
.object({
|
||||
Replicas: z.number().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const ReplicatedJobSchema = z
|
||||
.object({
|
||||
MaxConcurrent: z.number().optional(),
|
||||
TotalCompletions: z.number().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const ServiceModeSwarmSchema = z
|
||||
.object({
|
||||
Replicated: ReplicatedSchema.optional(),
|
||||
Global: z.object({}).optional(),
|
||||
ReplicatedJob: ReplicatedJobSchema.optional(),
|
||||
GlobalJob: z.object({}).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const NetworkSwarmSchema = z.array(
|
||||
z
|
||||
.object({
|
||||
Target: z.string().optional(),
|
||||
Aliases: z.array(z.string()).optional(),
|
||||
DriverOpts: z.object({}).optional(),
|
||||
})
|
||||
.strict(),
|
||||
);
|
||||
|
||||
const LabelsSwarmSchema = z.record(z.string());
|
||||
|
||||
const createSchema = createInsertSchema(applications, {
|
||||
appName: z.string(),
|
||||
createdAt: z.string(),
|
||||
@@ -124,6 +297,14 @@ const createSchema = createInsertSchema(applications, {
|
||||
"nixpacks",
|
||||
]),
|
||||
owner: z.string(),
|
||||
healthCheckSwarm: HealthCheckSwarmSchema.nullable(),
|
||||
restartPolicySwarm: RestartPolicySwarmSchema.nullable(),
|
||||
placementSwarm: PlacementSwarmSchema.nullable(),
|
||||
updateConfigSwarm: UpdateConfigSwarmSchema.nullable(),
|
||||
rollbackConfigSwarm: UpdateConfigSwarmSchema.nullable(),
|
||||
modeSwarm: ServiceModeSwarmSchema.nullable(),
|
||||
labelsSwarm: LabelsSwarmSchema.nullable(),
|
||||
networkSwarm: NetworkSwarmSchema.nullable(),
|
||||
});
|
||||
|
||||
export const apiCreateApplication = createSchema.pick({
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export * from "./application";
|
||||
export * from "./postgres";
|
||||
|
||||
export * from "./user";
|
||||
export * from "./admin";
|
||||
export * from "./auth";
|
||||
@@ -20,3 +19,4 @@ export * from "./security";
|
||||
export * from "./port";
|
||||
export * from "./redis";
|
||||
export * from "./shared";
|
||||
export * from "./registry";
|
||||
|
||||
98
server/db/schema/registry.ts
Normal file
98
server/db/schema/registry.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import { boolean, pgEnum, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||
import { auth } from "./auth";
|
||||
import { admins } from "./admin";
|
||||
import { z } from "zod";
|
||||
import { applications } from "./application";
|
||||
/**
|
||||
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
|
||||
* database instance for multiple projects.
|
||||
*
|
||||
* @see https://orm.drizzle.team/docs/goodies#multi-project-schema
|
||||
*/
|
||||
export const registryType = pgEnum("RegistryType", ["selfHosted", "cloud"]);
|
||||
|
||||
export const registry = pgTable("registry", {
|
||||
registryId: text("registryId")
|
||||
.notNull()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => nanoid()),
|
||||
registryName: text("registryName").notNull(),
|
||||
imagePrefix: text("imagePrefix"),
|
||||
username: text("username").notNull(),
|
||||
password: text("password").notNull(),
|
||||
registryUrl: text("registryUrl").notNull(),
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
registryType: registryType("selfHosted").notNull().default("cloud"),
|
||||
adminId: text("adminId")
|
||||
.notNull()
|
||||
.references(() => admins.adminId, { onDelete: "cascade" }),
|
||||
});
|
||||
|
||||
export const registryRelations = relations(registry, ({ one, many }) => ({
|
||||
admin: one(admins, {
|
||||
fields: [registry.adminId],
|
||||
references: [admins.adminId],
|
||||
}),
|
||||
applications: many(applications),
|
||||
}));
|
||||
|
||||
const createSchema = createInsertSchema(registry, {
|
||||
registryName: z.string().min(1),
|
||||
username: z.string().min(1),
|
||||
password: z.string().min(1),
|
||||
registryUrl: z.string().min(1),
|
||||
adminId: z.string().min(1),
|
||||
registryId: z.string().min(1),
|
||||
registryType: z.enum(["selfHosted", "cloud"]),
|
||||
imagePrefix: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export const apiCreateRegistry = createSchema
|
||||
.pick({})
|
||||
.extend({
|
||||
registryName: z.string().min(1),
|
||||
username: z.string().min(1),
|
||||
password: z.string().min(1),
|
||||
registryUrl: z.string(),
|
||||
registryType: z.enum(["selfHosted", "cloud"]),
|
||||
imagePrefix: z.string().nullable().optional(),
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiTestRegistry = createSchema.pick({}).extend({
|
||||
registryName: z.string().min(1),
|
||||
username: z.string().min(1),
|
||||
password: z.string().min(1),
|
||||
registryUrl: z.string(),
|
||||
registryType: z.enum(["selfHosted", "cloud"]),
|
||||
imagePrefix: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export const apiRemoveRegistry = createSchema
|
||||
.pick({
|
||||
registryId: true,
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiFindOneRegistry = createSchema
|
||||
.pick({
|
||||
registryId: true,
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiUpdateRegistry = createSchema.partial().extend({
|
||||
registryId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const apiEnableSelfHostedRegistry = createSchema
|
||||
.pick({
|
||||
registryUrl: true,
|
||||
username: true,
|
||||
password: true,
|
||||
})
|
||||
.required();
|
||||
Reference in New Issue
Block a user