mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-07-03 04:55:23 +02:00
Merge branch 'canary' into feature/stop-grace-period-2227
This commit is contained in:
@@ -24,7 +24,25 @@ import { redirects } from "./redirects";
|
||||
import { registry } from "./registry";
|
||||
import { security } from "./security";
|
||||
import { server } from "./server";
|
||||
import { applicationStatus, certificateType, triggerType } from "./shared";
|
||||
import {
|
||||
applicationStatus,
|
||||
certificateType,
|
||||
type HealthCheckSwarm,
|
||||
HealthCheckSwarmSchema,
|
||||
type LabelsSwarm,
|
||||
LabelsSwarmSchema,
|
||||
type NetworkSwarm,
|
||||
NetworkSwarmSchema,
|
||||
type PlacementSwarm,
|
||||
PlacementSwarmSchema,
|
||||
type RestartPolicySwarm,
|
||||
RestartPolicySwarmSchema,
|
||||
type ServiceModeSwarm,
|
||||
ServiceModeSwarmSchema,
|
||||
triggerType,
|
||||
type UpdateConfigSwarm,
|
||||
UpdateConfigSwarmSchema,
|
||||
} from "./shared";
|
||||
import { sshKeys } from "./ssh-key";
|
||||
import { generateAppName } from "./utils";
|
||||
export const sourceType = pgEnum("sourceType", [
|
||||
@@ -46,64 +64,6 @@ export const buildType = pgEnum("buildType", [
|
||||
"railpack",
|
||||
]);
|
||||
|
||||
export interface HealthCheckSwarm {
|
||||
Test?: string[] | undefined;
|
||||
Interval?: number | undefined;
|
||||
Timeout?: number | undefined;
|
||||
StartPeriod?: number | undefined;
|
||||
Retries?: number | undefined;
|
||||
}
|
||||
|
||||
export interface RestartPolicySwarm {
|
||||
Condition?: string | undefined;
|
||||
Delay?: number | undefined;
|
||||
MaxAttempts?: number | undefined;
|
||||
Window?: number | undefined;
|
||||
}
|
||||
|
||||
export interface PlacementSwarm {
|
||||
Constraints?: string[] | undefined;
|
||||
Preferences?: Array<{ Spread: { SpreadDescriptor: string } }> | undefined;
|
||||
MaxReplicas?: number | undefined;
|
||||
Platforms?:
|
||||
| Array<{
|
||||
Architecture: string;
|
||||
OS: string;
|
||||
}>
|
||||
| undefined;
|
||||
}
|
||||
|
||||
export interface UpdateConfigSwarm {
|
||||
Parallelism: number;
|
||||
Delay?: number | undefined;
|
||||
FailureAction?: string | undefined;
|
||||
Monitor?: number | undefined;
|
||||
MaxFailureRatio?: number | undefined;
|
||||
Order: string;
|
||||
}
|
||||
|
||||
export interface ServiceModeSwarm {
|
||||
Replicated?: { Replicas?: number | undefined } | undefined;
|
||||
Global?: {} | undefined;
|
||||
ReplicatedJob?:
|
||||
| {
|
||||
MaxConcurrent?: number | undefined;
|
||||
TotalCompletions?: number | undefined;
|
||||
}
|
||||
| undefined;
|
||||
GlobalJob?: {} | undefined;
|
||||
}
|
||||
|
||||
export interface NetworkSwarm {
|
||||
Target?: string | undefined;
|
||||
Aliases?: string[] | undefined;
|
||||
DriverOpts?: { [key: string]: string } | undefined;
|
||||
}
|
||||
|
||||
export interface LabelsSwarm {
|
||||
[name: string]: string;
|
||||
}
|
||||
|
||||
export const applications = pgTable("application", {
|
||||
applicationId: text("applicationId")
|
||||
.notNull()
|
||||
@@ -209,6 +169,7 @@ export const applications = pgTable("application", {
|
||||
.notNull()
|
||||
.default("idle"),
|
||||
buildType: buildType("buildType").notNull().default("nixpacks"),
|
||||
railpackVersion: text("railpackVersion").default("0.2.2"),
|
||||
herokuVersion: text("herokuVersion").default("24"),
|
||||
publishDirectory: text("publishDirectory"),
|
||||
isStaticSpa: boolean("isStaticSpa"),
|
||||
@@ -283,94 +244,6 @@ export const applicationsRelations = relations(
|
||||
}),
|
||||
);
|
||||
|
||||
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(),
|
||||
@@ -413,6 +286,7 @@ const createSchema = createInsertSchema(applications, {
|
||||
"static",
|
||||
"railpack",
|
||||
]),
|
||||
railpackVersion: z.string().optional(),
|
||||
herokuVersion: z.string().optional(),
|
||||
publishDirectory: z.string().optional(),
|
||||
isStaticSpa: z.boolean().optional(),
|
||||
@@ -468,6 +342,7 @@ export const apiSaveBuildType = createSchema
|
||||
dockerContextPath: true,
|
||||
dockerBuildStage: true,
|
||||
herokuVersion: true,
|
||||
railpackVersion: true,
|
||||
})
|
||||
.required()
|
||||
.merge(createSchema.pick({ publishDirectory: true, isStaticSpa: true }));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import { integer, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { integer, json, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
@@ -7,7 +7,23 @@ import { backups } from "./backups";
|
||||
import { mounts } from "./mount";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
import { applicationStatus } from "./shared";
|
||||
import {
|
||||
applicationStatus,
|
||||
type HealthCheckSwarm,
|
||||
HealthCheckSwarmSchema,
|
||||
type LabelsSwarm,
|
||||
LabelsSwarmSchema,
|
||||
type NetworkSwarm,
|
||||
NetworkSwarmSchema,
|
||||
type PlacementSwarm,
|
||||
PlacementSwarmSchema,
|
||||
type RestartPolicySwarm,
|
||||
RestartPolicySwarmSchema,
|
||||
type ServiceModeSwarm,
|
||||
ServiceModeSwarmSchema,
|
||||
type UpdateConfigSwarm,
|
||||
UpdateConfigSwarmSchema,
|
||||
} from "./shared";
|
||||
import { generateAppName } from "./utils";
|
||||
|
||||
export const mariadb = pgTable("mariadb", {
|
||||
@@ -38,6 +54,15 @@ export const mariadb = pgTable("mariadb", {
|
||||
applicationStatus: applicationStatus("applicationStatus")
|
||||
.notNull()
|
||||
.default("idle"),
|
||||
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(),
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
@@ -83,6 +108,14 @@ const createSchema = createInsertSchema(mariadb, {
|
||||
externalPort: z.number(),
|
||||
description: z.string().optional(),
|
||||
serverId: z.string().optional(),
|
||||
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 apiCreateMariaDB = createSchema
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import { boolean, integer, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { boolean, integer, json, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
@@ -7,7 +7,23 @@ import { backups } from "./backups";
|
||||
import { mounts } from "./mount";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
import { applicationStatus } from "./shared";
|
||||
import {
|
||||
applicationStatus,
|
||||
type HealthCheckSwarm,
|
||||
HealthCheckSwarmSchema,
|
||||
type LabelsSwarm,
|
||||
LabelsSwarmSchema,
|
||||
type NetworkSwarm,
|
||||
NetworkSwarmSchema,
|
||||
type PlacementSwarm,
|
||||
PlacementSwarmSchema,
|
||||
type RestartPolicySwarm,
|
||||
RestartPolicySwarmSchema,
|
||||
type ServiceModeSwarm,
|
||||
ServiceModeSwarmSchema,
|
||||
type UpdateConfigSwarm,
|
||||
UpdateConfigSwarmSchema,
|
||||
} from "./shared";
|
||||
import { generateAppName } from "./utils";
|
||||
|
||||
export const mongo = pgTable("mongo", {
|
||||
@@ -34,6 +50,15 @@ export const mongo = pgTable("mongo", {
|
||||
applicationStatus: applicationStatus("applicationStatus")
|
||||
.notNull()
|
||||
.default("idle"),
|
||||
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(),
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
@@ -79,6 +104,14 @@ const createSchema = createInsertSchema(mongo, {
|
||||
description: z.string().optional(),
|
||||
serverId: z.string().optional(),
|
||||
replicaSets: z.boolean().default(false),
|
||||
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 apiCreateMongo = createSchema
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import { integer, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { integer, json, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
@@ -7,7 +7,23 @@ import { backups } from "./backups";
|
||||
import { mounts } from "./mount";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
import { applicationStatus } from "./shared";
|
||||
import {
|
||||
applicationStatus,
|
||||
type HealthCheckSwarm,
|
||||
HealthCheckSwarmSchema,
|
||||
type LabelsSwarm,
|
||||
LabelsSwarmSchema,
|
||||
type NetworkSwarm,
|
||||
NetworkSwarmSchema,
|
||||
type PlacementSwarm,
|
||||
PlacementSwarmSchema,
|
||||
type RestartPolicySwarm,
|
||||
RestartPolicySwarmSchema,
|
||||
type ServiceModeSwarm,
|
||||
ServiceModeSwarmSchema,
|
||||
type UpdateConfigSwarm,
|
||||
UpdateConfigSwarmSchema,
|
||||
} from "./shared";
|
||||
import { generateAppName } from "./utils";
|
||||
|
||||
export const mysql = pgTable("mysql", {
|
||||
@@ -36,6 +52,15 @@ export const mysql = pgTable("mysql", {
|
||||
applicationStatus: applicationStatus("applicationStatus")
|
||||
.notNull()
|
||||
.default("idle"),
|
||||
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(),
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
@@ -81,6 +106,14 @@ const createSchema = createInsertSchema(mysql, {
|
||||
externalPort: z.number(),
|
||||
description: z.string().optional(),
|
||||
serverId: z.string().optional(),
|
||||
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 apiCreateMySql = createSchema
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import { integer, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { integer, json, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
@@ -7,7 +7,23 @@ import { backups } from "./backups";
|
||||
import { mounts } from "./mount";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
import { applicationStatus } from "./shared";
|
||||
import {
|
||||
applicationStatus,
|
||||
type HealthCheckSwarm,
|
||||
HealthCheckSwarmSchema,
|
||||
type LabelsSwarm,
|
||||
LabelsSwarmSchema,
|
||||
type NetworkSwarm,
|
||||
NetworkSwarmSchema,
|
||||
type PlacementSwarm,
|
||||
PlacementSwarmSchema,
|
||||
type RestartPolicySwarm,
|
||||
RestartPolicySwarmSchema,
|
||||
type ServiceModeSwarm,
|
||||
ServiceModeSwarmSchema,
|
||||
type UpdateConfigSwarm,
|
||||
UpdateConfigSwarmSchema,
|
||||
} from "./shared";
|
||||
import { generateAppName } from "./utils";
|
||||
|
||||
export const postgres = pgTable("postgres", {
|
||||
@@ -35,6 +51,16 @@ export const postgres = pgTable("postgres", {
|
||||
applicationStatus: applicationStatus("applicationStatus")
|
||||
.notNull()
|
||||
.default("idle"),
|
||||
|
||||
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(),
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
@@ -78,6 +104,14 @@ const createSchema = createInsertSchema(postgres, {
|
||||
createdAt: z.string(),
|
||||
description: z.string().optional(),
|
||||
serverId: z.string().optional(),
|
||||
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 apiCreatePostgres = createSchema
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import { integer, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { integer, json, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { mounts } from "./mount";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
import { applicationStatus } from "./shared";
|
||||
import {
|
||||
applicationStatus,
|
||||
type HealthCheckSwarm,
|
||||
HealthCheckSwarmSchema,
|
||||
type LabelsSwarm,
|
||||
LabelsSwarmSchema,
|
||||
type NetworkSwarm,
|
||||
NetworkSwarmSchema,
|
||||
type PlacementSwarm,
|
||||
PlacementSwarmSchema,
|
||||
type RestartPolicySwarm,
|
||||
RestartPolicySwarmSchema,
|
||||
type ServiceModeSwarm,
|
||||
ServiceModeSwarmSchema,
|
||||
type UpdateConfigSwarm,
|
||||
UpdateConfigSwarmSchema,
|
||||
} from "./shared";
|
||||
import { generateAppName } from "./utils";
|
||||
|
||||
export const redis = pgTable("redis", {
|
||||
@@ -35,6 +51,15 @@ export const redis = pgTable("redis", {
|
||||
applicationStatus: applicationStatus("applicationStatus")
|
||||
.notNull()
|
||||
.default("idle"),
|
||||
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(),
|
||||
projectId: text("projectId")
|
||||
.notNull()
|
||||
.references(() => projects.projectId, { onDelete: "cascade" }),
|
||||
@@ -73,6 +98,14 @@ const createSchema = createInsertSchema(redis, {
|
||||
externalPort: z.number(),
|
||||
description: z.string().optional(),
|
||||
serverId: z.string().optional(),
|
||||
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 apiCreateRedis = createSchema
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { pgEnum } from "drizzle-orm/pg-core";
|
||||
import { z } from "zod";
|
||||
|
||||
export const applicationStatus = pgEnum("applicationStatus", [
|
||||
"idle",
|
||||
@@ -14,3 +15,149 @@ export const certificateType = pgEnum("certificateType", [
|
||||
]);
|
||||
|
||||
export const triggerType = pgEnum("triggerType", ["push", "tag"]);
|
||||
|
||||
export interface HealthCheckSwarm {
|
||||
Test?: string[] | undefined;
|
||||
Interval?: number | undefined;
|
||||
Timeout?: number | undefined;
|
||||
StartPeriod?: number | undefined;
|
||||
Retries?: number | undefined;
|
||||
}
|
||||
|
||||
export interface RestartPolicySwarm {
|
||||
Condition?: string | undefined;
|
||||
Delay?: number | undefined;
|
||||
MaxAttempts?: number | undefined;
|
||||
Window?: number | undefined;
|
||||
}
|
||||
|
||||
export interface PlacementSwarm {
|
||||
Constraints?: string[] | undefined;
|
||||
Preferences?: Array<{ Spread: { SpreadDescriptor: string } }> | undefined;
|
||||
MaxReplicas?: number | undefined;
|
||||
Platforms?:
|
||||
| Array<{
|
||||
Architecture: string;
|
||||
OS: string;
|
||||
}>
|
||||
| undefined;
|
||||
}
|
||||
|
||||
export interface UpdateConfigSwarm {
|
||||
Parallelism: number;
|
||||
Delay?: number | undefined;
|
||||
FailureAction?: string | undefined;
|
||||
Monitor?: number | undefined;
|
||||
MaxFailureRatio?: number | undefined;
|
||||
Order: string;
|
||||
}
|
||||
|
||||
export interface ServiceModeSwarm {
|
||||
Replicated?: { Replicas?: number | undefined } | undefined;
|
||||
Global?: {} | undefined;
|
||||
ReplicatedJob?:
|
||||
| {
|
||||
MaxConcurrent?: number | undefined;
|
||||
TotalCompletions?: number | undefined;
|
||||
}
|
||||
| undefined;
|
||||
GlobalJob?: {} | undefined;
|
||||
}
|
||||
|
||||
export interface NetworkSwarm {
|
||||
Target?: string | undefined;
|
||||
Aliases?: string[] | undefined;
|
||||
DriverOpts?: { [key: string]: string } | undefined;
|
||||
}
|
||||
|
||||
export interface LabelsSwarm {
|
||||
[name: string]: string;
|
||||
}
|
||||
|
||||
export 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();
|
||||
|
||||
export const RestartPolicySwarmSchema = z
|
||||
.object({
|
||||
Condition: z.string().optional(),
|
||||
Delay: z.number().optional(),
|
||||
MaxAttempts: z.number().optional(),
|
||||
Window: z.number().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const PreferenceSchema = z
|
||||
.object({
|
||||
Spread: z.object({
|
||||
SpreadDescriptor: z.string(),
|
||||
}),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const PlatformSchema = z
|
||||
.object({
|
||||
Architecture: z.string(),
|
||||
OS: z.string(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export 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();
|
||||
|
||||
export 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();
|
||||
|
||||
export const ReplicatedSchema = z
|
||||
.object({
|
||||
Replicas: z.number().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const ReplicatedJobSchema = z
|
||||
.object({
|
||||
MaxConcurrent: z.number().optional(),
|
||||
TotalCompletions: z.number().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const ServiceModeSwarmSchema = z
|
||||
.object({
|
||||
Replicated: ReplicatedSchema.optional(),
|
||||
Global: z.object({}).optional(),
|
||||
ReplicatedJob: ReplicatedJobSchema.optional(),
|
||||
GlobalJob: z.object({}).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const NetworkSwarmSchema = z.array(
|
||||
z
|
||||
.object({
|
||||
Target: z.string().optional(),
|
||||
Aliases: z.array(z.string()).optional(),
|
||||
DriverOpts: z.object({}).optional(),
|
||||
})
|
||||
.strict(),
|
||||
);
|
||||
|
||||
export const LabelsSwarmSchema = z.record(z.string());
|
||||
|
||||
@@ -29,6 +29,9 @@ const { handler, api } = betterAuth({
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
|
||||
},
|
||||
},
|
||||
logger: {
|
||||
disabled: process.env.NODE_ENV === "production",
|
||||
},
|
||||
...(!IS_CLOUD && {
|
||||
async trustedOrigins() {
|
||||
const admin = await db.query.member.findFirst({
|
||||
|
||||
@@ -5,6 +5,11 @@ import {
|
||||
execAsync,
|
||||
execAsyncRemote,
|
||||
} from "@dokploy/server/utils/process/execAsync";
|
||||
import {
|
||||
initializeStandaloneTraefik,
|
||||
initializeTraefikService,
|
||||
type TraefikOptions,
|
||||
} from "../setup/traefik-setup";
|
||||
|
||||
export interface IUpdateData {
|
||||
latestVersion: string | null;
|
||||
@@ -243,3 +248,165 @@ export const cleanupFullDocker = async (serverId?: string | null) => {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const getDockerResourceType = async (
|
||||
resourceName: string,
|
||||
serverId?: string,
|
||||
) => {
|
||||
let result = "";
|
||||
const command = `
|
||||
RESOURCE_NAME="${resourceName}"
|
||||
if docker service inspect "$RESOURCE_NAME" &>/dev/null; then
|
||||
echo "service"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if docker inspect "$RESOURCE_NAME" &>/dev/null; then
|
||||
echo "standalone"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "unknown"
|
||||
exit 0
|
||||
`;
|
||||
|
||||
if (serverId) {
|
||||
const { stdout } = await execAsyncRemote(serverId, command);
|
||||
result = stdout.trim();
|
||||
} else {
|
||||
const { stdout } = await execAsync(command);
|
||||
result = stdout.trim();
|
||||
}
|
||||
if (result === "service") {
|
||||
return "service";
|
||||
}
|
||||
if (result === "standalone") {
|
||||
return "standalone";
|
||||
}
|
||||
return "unknown";
|
||||
};
|
||||
|
||||
export const reloadDockerResource = async (
|
||||
resourceName: string,
|
||||
serverId?: string,
|
||||
) => {
|
||||
const resourceType = await getDockerResourceType(resourceName, serverId);
|
||||
let command = "";
|
||||
if (resourceType === "service") {
|
||||
command = `docker service update --force ${resourceName}`;
|
||||
} else {
|
||||
command = `docker restart ${resourceName}`;
|
||||
}
|
||||
if (serverId) {
|
||||
await execAsyncRemote(serverId, command);
|
||||
} else {
|
||||
await execAsync(command);
|
||||
}
|
||||
};
|
||||
|
||||
export const readEnvironmentVariables = async (
|
||||
resourceName: string,
|
||||
serverId?: string,
|
||||
) => {
|
||||
const resourceType = await getDockerResourceType(resourceName, serverId);
|
||||
let command = "";
|
||||
if (resourceType === "service") {
|
||||
command = `docker service inspect ${resourceName} --format '{{json .Spec.TaskTemplate.ContainerSpec.Env}}'`;
|
||||
} else {
|
||||
command = `docker container inspect ${resourceName} --format '{{json .Config.Env}}'`;
|
||||
}
|
||||
let result = "";
|
||||
if (serverId) {
|
||||
const { stdout } = await execAsyncRemote(serverId, command);
|
||||
result = stdout.trim();
|
||||
} else {
|
||||
const { stdout } = await execAsync(command);
|
||||
result = stdout.trim();
|
||||
}
|
||||
if (result === "null") {
|
||||
return "";
|
||||
}
|
||||
return JSON.parse(result)?.join("\n");
|
||||
};
|
||||
|
||||
export const readPorts = async (
|
||||
resourceName: string,
|
||||
serverId?: string,
|
||||
): Promise<
|
||||
{ targetPort: number; publishedPort: number; protocol?: string }[]
|
||||
> => {
|
||||
const resourceType = await getDockerResourceType(resourceName, serverId);
|
||||
let command = "";
|
||||
if (resourceType === "service") {
|
||||
command = `docker service inspect ${resourceName} --format '{{json .Spec.EndpointSpec.Ports}}'`;
|
||||
} else {
|
||||
command = `docker container inspect ${resourceName} --format '{{json .NetworkSettings.Ports}}'`;
|
||||
}
|
||||
let result = "";
|
||||
if (serverId) {
|
||||
const { stdout } = await execAsyncRemote(serverId, command);
|
||||
result = stdout.trim();
|
||||
} else {
|
||||
const { stdout } = await execAsync(command);
|
||||
result = stdout.trim();
|
||||
}
|
||||
|
||||
if (result === "null") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const parsedResult = JSON.parse(result);
|
||||
|
||||
if (resourceType === "service") {
|
||||
return parsedResult
|
||||
.map((port: any) => ({
|
||||
targetPort: port.TargetPort,
|
||||
publishedPort: port.PublishedPort,
|
||||
protocol: port.Protocol,
|
||||
}))
|
||||
.filter((port: any) => port.targetPort !== 80 && port.targetPort !== 443);
|
||||
}
|
||||
const ports: {
|
||||
targetPort: number;
|
||||
publishedPort: number;
|
||||
protocol?: string;
|
||||
}[] = [];
|
||||
for (const key in parsedResult) {
|
||||
if (Object.hasOwn(parsedResult, key)) {
|
||||
const containerPortMapppings = parsedResult[key];
|
||||
const protocol = key.split("/")[1];
|
||||
const targetPort = Number.parseInt(key.split("/")[0] ?? "0", 10);
|
||||
|
||||
containerPortMapppings.forEach((mapping: any) => {
|
||||
ports.push({
|
||||
targetPort: targetPort,
|
||||
publishedPort: Number.parseInt(mapping.HostPort, 10),
|
||||
protocol: protocol,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
return ports.filter(
|
||||
(port: any) => port.targetPort !== 80 && port.targetPort !== 443,
|
||||
);
|
||||
};
|
||||
|
||||
export const writeTraefikSetup = async (
|
||||
input: TraefikOptions,
|
||||
serverId?: string,
|
||||
) => {
|
||||
const resourceType = await getDockerResourceType("dokploy-traefik", serverId);
|
||||
if (resourceType === "service") {
|
||||
await initializeTraefikService({
|
||||
env: input.env,
|
||||
additionalPorts: input.additionalPorts,
|
||||
serverId: serverId,
|
||||
});
|
||||
} else {
|
||||
await initializeStandaloneTraefik({
|
||||
env: input.env,
|
||||
additionalPorts: input.additionalPorts,
|
||||
serverId: serverId,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import type { ContainerCreateOptions } from "dockerode";
|
||||
import type { ContainerCreateOptions, CreateServiceOptions } from "dockerode";
|
||||
import { dump } from "js-yaml";
|
||||
import { paths } from "../constants";
|
||||
import { getRemoteDocker } from "../utils/servers/remote-docker";
|
||||
@@ -15,23 +15,20 @@ export const TRAEFIK_HTTP3_PORT =
|
||||
Number.parseInt(process.env.TRAEFIK_HTTP3_PORT!, 10) || 443;
|
||||
export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.1.2";
|
||||
|
||||
interface TraefikOptions {
|
||||
enableDashboard?: boolean;
|
||||
export interface TraefikOptions {
|
||||
env?: string[];
|
||||
serverId?: string;
|
||||
additionalPorts?: {
|
||||
targetPort: number;
|
||||
publishedPort: number;
|
||||
protocol?: string;
|
||||
}[];
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
export const initializeTraefik = async ({
|
||||
enableDashboard = false,
|
||||
export const initializeStandaloneTraefik = async ({
|
||||
env,
|
||||
serverId,
|
||||
additionalPorts = [],
|
||||
force = false,
|
||||
}: TraefikOptions = {}) => {
|
||||
const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId);
|
||||
const imageName = `traefik:v${TRAEFIK_VERSION}`;
|
||||
@@ -51,13 +48,17 @@ export const initializeTraefik = async ({
|
||||
],
|
||||
};
|
||||
|
||||
const enableDashboard = additionalPorts.some(
|
||||
(port) => port.targetPort === 8080,
|
||||
);
|
||||
|
||||
if (enableDashboard) {
|
||||
exposedPorts["8080/tcp"] = {};
|
||||
portBindings["8080/tcp"] = [{ HostPort: "8080" }];
|
||||
}
|
||||
|
||||
for (const port of additionalPorts) {
|
||||
const portKey = `${port.targetPort}/tcp`;
|
||||
const portKey = `${port.targetPort}/${port.protocol ?? "tcp"}`;
|
||||
exposedPorts[portKey] = {};
|
||||
portBindings[portKey] = [{ HostPort: port.publishedPort.toString() }];
|
||||
}
|
||||
@@ -87,68 +88,117 @@ export const initializeTraefik = async ({
|
||||
|
||||
const docker = await getRemoteDocker(serverId);
|
||||
try {
|
||||
try {
|
||||
const service = docker.getService("dokploy-traefik");
|
||||
await service?.remove({ force: true });
|
||||
|
||||
let attempts = 0;
|
||||
const maxAttempts = 5;
|
||||
while (attempts < maxAttempts) {
|
||||
try {
|
||||
await docker.listServices({
|
||||
filters: { name: ["dokploy-traefik"] },
|
||||
});
|
||||
console.log("Waiting for service cleanup...");
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
attempts++;
|
||||
} catch {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
console.log("No existing service to remove");
|
||||
}
|
||||
|
||||
// Then try to remove any existing container
|
||||
const container = docker.getContainer(containerName);
|
||||
try {
|
||||
const inspect = await container.inspect();
|
||||
if (inspect.State.Status === "running" && !force) {
|
||||
console.log("Traefik already running");
|
||||
return;
|
||||
}
|
||||
|
||||
await container.remove({ force: true });
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
} catch {
|
||||
console.log("No existing container to remove");
|
||||
}
|
||||
|
||||
// Create and start the new container
|
||||
try {
|
||||
await docker.createContainer(settings);
|
||||
const newContainer = docker.getContainer(containerName);
|
||||
await newContainer.start();
|
||||
console.log("Traefik container started successfully");
|
||||
} catch (error: any) {
|
||||
if (error?.json?.message?.includes("port is already allocated")) {
|
||||
console.log("Ports still in use, waiting longer for cleanup...");
|
||||
await new Promise((resolve) => setTimeout(resolve, 10000));
|
||||
// Try one more time
|
||||
await docker.createContainer(settings);
|
||||
const newContainer = docker.getContainer(containerName);
|
||||
await newContainer.start();
|
||||
console.log("Traefik container started successfully after retry");
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
console.log("Traefik Started ✅");
|
||||
} catch (error) {
|
||||
console.error("Error in initializeStandaloneTraefik", error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize Traefik:", error);
|
||||
await docker.createContainer(settings);
|
||||
console.error("Error in initializeStandaloneTraefik", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const initializeTraefikService = async ({
|
||||
env,
|
||||
additionalPorts = [],
|
||||
serverId,
|
||||
}: TraefikOptions) => {
|
||||
const { MAIN_TRAEFIK_PATH, DYNAMIC_TRAEFIK_PATH } = paths(!!serverId);
|
||||
const imageName = `traefik:v${TRAEFIK_VERSION}`;
|
||||
const appName = "dokploy-traefik";
|
||||
|
||||
const settings: CreateServiceOptions = {
|
||||
Name: appName,
|
||||
TaskTemplate: {
|
||||
ContainerSpec: {
|
||||
Image: imageName,
|
||||
Env: env,
|
||||
Mounts: [
|
||||
{
|
||||
Type: "bind",
|
||||
Source: `${MAIN_TRAEFIK_PATH}/traefik.yml`,
|
||||
Target: "/etc/traefik/traefik.yml",
|
||||
},
|
||||
{
|
||||
Type: "bind",
|
||||
Source: DYNAMIC_TRAEFIK_PATH,
|
||||
Target: "/etc/dokploy/traefik/dynamic",
|
||||
},
|
||||
{
|
||||
Type: "bind",
|
||||
Source: "/var/run/docker.sock",
|
||||
Target: "/var/run/docker.sock",
|
||||
},
|
||||
],
|
||||
},
|
||||
Networks: [{ Target: "dokploy-network" }],
|
||||
Placement: {
|
||||
Constraints: ["node.role==manager"],
|
||||
},
|
||||
},
|
||||
Mode: {
|
||||
Replicated: {
|
||||
Replicas: 1,
|
||||
},
|
||||
},
|
||||
EndpointSpec: {
|
||||
Ports: [
|
||||
{
|
||||
TargetPort: 443,
|
||||
PublishedPort: TRAEFIK_SSL_PORT,
|
||||
PublishMode: "host",
|
||||
Protocol: "tcp",
|
||||
},
|
||||
{
|
||||
TargetPort: 443,
|
||||
PublishedPort: TRAEFIK_SSL_PORT,
|
||||
PublishMode: "host",
|
||||
Protocol: "udp",
|
||||
},
|
||||
{
|
||||
TargetPort: 80,
|
||||
PublishedPort: TRAEFIK_PORT,
|
||||
PublishMode: "host",
|
||||
Protocol: "tcp",
|
||||
},
|
||||
|
||||
...additionalPorts.map((port) => ({
|
||||
TargetPort: port.targetPort,
|
||||
PublishedPort: port.publishedPort,
|
||||
Protocol: port.protocol as "tcp" | "udp" | "sctp" | undefined,
|
||||
PublishMode: "host" as const,
|
||||
})),
|
||||
],
|
||||
},
|
||||
};
|
||||
const docker = await getRemoteDocker(serverId);
|
||||
try {
|
||||
const service = docker.getService(appName);
|
||||
const inspect = await service.inspect();
|
||||
|
||||
await service.update({
|
||||
version: Number.parseInt(inspect.Version.Index),
|
||||
...settings,
|
||||
TaskTemplate: {
|
||||
...settings.TaskTemplate,
|
||||
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
|
||||
},
|
||||
});
|
||||
console.log("Traefik Updated ✅");
|
||||
} catch {
|
||||
await docker.createService(settings);
|
||||
console.log("Traefik Started ✅");
|
||||
}
|
||||
};
|
||||
|
||||
export const createDefaultServerTraefikConfig = () => {
|
||||
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
||||
const configFilePath = path.join(DYNAMIC_TRAEFIK_PATH, "dokploy.yml");
|
||||
|
||||
@@ -75,7 +75,7 @@ export const buildRailpack = async (
|
||||
]
|
||||
: []),
|
||||
"--build-arg",
|
||||
"BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v0.2.2",
|
||||
`BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v${application.railpackVersion}`,
|
||||
"-f",
|
||||
`${buildAppDirectory}/railpack-plan.json`,
|
||||
"--output",
|
||||
@@ -110,6 +110,8 @@ export const buildRailpack = async (
|
||||
return true;
|
||||
} catch (e) {
|
||||
throw e;
|
||||
} finally {
|
||||
await execAsync("docker buildx rm builder-containerd");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -155,7 +157,7 @@ export const getRailpackCommand = (
|
||||
]
|
||||
: []),
|
||||
"--build-arg",
|
||||
"BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v0.0.64",
|
||||
`BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v${application.railpackVersion}`,
|
||||
"-f",
|
||||
`${buildAppDirectory}/railpack-plan.json`,
|
||||
"--output",
|
||||
@@ -194,6 +196,7 @@ docker ${buildArgs.join(" ")} >> ${logPath} 2>> ${logPath} || {
|
||||
exit 1;
|
||||
}
|
||||
echo "✅ Railpack build completed." >> ${logPath};
|
||||
docker buildx rm builder-containerd
|
||||
`;
|
||||
|
||||
return bashCommand;
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { CreateServiceOptions } from "dockerode";
|
||||
import {
|
||||
calculateResources,
|
||||
generateBindMounts,
|
||||
generateConfigContainer,
|
||||
generateFileMounts,
|
||||
generateVolumeMounts,
|
||||
prepareEnvironmentVariables,
|
||||
@@ -34,6 +35,17 @@ export const buildMariadb = async (mariadb: MariadbNested) => {
|
||||
const defaultMariadbEnv = `MARIADB_DATABASE="${databaseName}"\nMARIADB_USER="${databaseUser}"\nMARIADB_PASSWORD="${databasePassword}"\nMARIADB_ROOT_PASSWORD="${databaseRootPassword}"${
|
||||
env ? `\n${env}` : ""
|
||||
}`;
|
||||
|
||||
const {
|
||||
HealthCheck,
|
||||
RestartPolicy,
|
||||
Placement,
|
||||
Labels,
|
||||
Mode,
|
||||
RollbackConfig,
|
||||
UpdateConfig,
|
||||
Networks,
|
||||
} = generateConfigContainer(mariadb);
|
||||
const resources = calculateResources({
|
||||
memoryLimit,
|
||||
memoryReservation,
|
||||
@@ -54,6 +66,7 @@ export const buildMariadb = async (mariadb: MariadbNested) => {
|
||||
Name: appName,
|
||||
TaskTemplate: {
|
||||
ContainerSpec: {
|
||||
HealthCheck,
|
||||
Image: dockerImage,
|
||||
Env: envVariables,
|
||||
Mounts: [...volumesMount, ...bindsMount, ...filesMount],
|
||||
@@ -63,20 +76,17 @@ export const buildMariadb = async (mariadb: MariadbNested) => {
|
||||
Args: ["-c", command],
|
||||
}
|
||||
: {}),
|
||||
Labels,
|
||||
},
|
||||
Networks: [{ Target: "dokploy-network" }],
|
||||
Networks,
|
||||
RestartPolicy,
|
||||
Placement,
|
||||
Resources: {
|
||||
...resources,
|
||||
},
|
||||
Placement: {
|
||||
Constraints: ["node.role==manager"],
|
||||
},
|
||||
},
|
||||
Mode: {
|
||||
Replicated: {
|
||||
Replicas: 1,
|
||||
},
|
||||
},
|
||||
Mode,
|
||||
RollbackConfig,
|
||||
EndpointSpec: {
|
||||
Mode: "dnsrr",
|
||||
Ports: externalPort
|
||||
@@ -90,6 +100,7 @@ export const buildMariadb = async (mariadb: MariadbNested) => {
|
||||
]
|
||||
: [],
|
||||
},
|
||||
UpdateConfig,
|
||||
};
|
||||
try {
|
||||
const service = docker.getService(appName);
|
||||
@@ -97,6 +108,10 @@ export const buildMariadb = async (mariadb: MariadbNested) => {
|
||||
await service.update({
|
||||
version: Number.parseInt(inspect.Version.Index),
|
||||
...settings,
|
||||
TaskTemplate: {
|
||||
...settings.TaskTemplate,
|
||||
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
await docker.createService(settings);
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { CreateServiceOptions } from "dockerode";
|
||||
import {
|
||||
calculateResources,
|
||||
generateBindMounts,
|
||||
generateConfigContainer,
|
||||
generateFileMounts,
|
||||
generateVolumeMounts,
|
||||
prepareEnvironmentVariables,
|
||||
@@ -81,6 +82,17 @@ ${command ?? "wait $MONGOD_PID"}`;
|
||||
env ? `\n${env}` : ""
|
||||
}`;
|
||||
|
||||
const {
|
||||
HealthCheck,
|
||||
RestartPolicy,
|
||||
Placement,
|
||||
Labels,
|
||||
Mode,
|
||||
RollbackConfig,
|
||||
UpdateConfig,
|
||||
Networks,
|
||||
} = generateConfigContainer(mongo);
|
||||
|
||||
const resources = calculateResources({
|
||||
memoryLimit,
|
||||
memoryReservation,
|
||||
@@ -102,6 +114,7 @@ ${command ?? "wait $MONGOD_PID"}`;
|
||||
Name: appName,
|
||||
TaskTemplate: {
|
||||
ContainerSpec: {
|
||||
HealthCheck,
|
||||
Image: dockerImage,
|
||||
Env: envVariables,
|
||||
Mounts: [...volumesMount, ...bindsMount, ...filesMount],
|
||||
@@ -116,20 +129,17 @@ ${command ?? "wait $MONGOD_PID"}`;
|
||||
Args: ["-c", command],
|
||||
}),
|
||||
}),
|
||||
Labels,
|
||||
},
|
||||
Networks: [{ Target: "dokploy-network" }],
|
||||
Networks,
|
||||
RestartPolicy,
|
||||
Placement,
|
||||
Resources: {
|
||||
...resources,
|
||||
},
|
||||
Placement: {
|
||||
Constraints: ["node.role==manager"],
|
||||
},
|
||||
},
|
||||
Mode: {
|
||||
Replicated: {
|
||||
Replicas: 1,
|
||||
},
|
||||
},
|
||||
Mode,
|
||||
RollbackConfig,
|
||||
EndpointSpec: {
|
||||
Mode: "dnsrr",
|
||||
Ports: externalPort
|
||||
@@ -143,6 +153,7 @@ ${command ?? "wait $MONGOD_PID"}`;
|
||||
]
|
||||
: [],
|
||||
},
|
||||
UpdateConfig,
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -151,6 +162,10 @@ ${command ?? "wait $MONGOD_PID"}`;
|
||||
await service.update({
|
||||
version: Number.parseInt(inspect.Version.Index),
|
||||
...settings,
|
||||
TaskTemplate: {
|
||||
...settings.TaskTemplate,
|
||||
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
await docker.createService(settings);
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { CreateServiceOptions } from "dockerode";
|
||||
import {
|
||||
calculateResources,
|
||||
generateBindMounts,
|
||||
generateConfigContainer,
|
||||
generateFileMounts,
|
||||
generateVolumeMounts,
|
||||
prepareEnvironmentVariables,
|
||||
@@ -40,6 +41,17 @@ export const buildMysql = async (mysql: MysqlNested) => {
|
||||
: `MYSQL_DATABASE="${databaseName}"\nMYSQL_ROOT_PASSWORD="${databaseRootPassword}"${
|
||||
env ? `\n${env}` : ""
|
||||
}`;
|
||||
|
||||
const {
|
||||
HealthCheck,
|
||||
RestartPolicy,
|
||||
Placement,
|
||||
Labels,
|
||||
Mode,
|
||||
RollbackConfig,
|
||||
UpdateConfig,
|
||||
Networks,
|
||||
} = generateConfigContainer(mysql);
|
||||
const resources = calculateResources({
|
||||
memoryLimit,
|
||||
memoryReservation,
|
||||
@@ -60,6 +72,7 @@ export const buildMysql = async (mysql: MysqlNested) => {
|
||||
Name: appName,
|
||||
TaskTemplate: {
|
||||
ContainerSpec: {
|
||||
HealthCheck,
|
||||
Image: dockerImage,
|
||||
Env: envVariables,
|
||||
Mounts: [...volumesMount, ...bindsMount, ...filesMount],
|
||||
@@ -69,20 +82,17 @@ export const buildMysql = async (mysql: MysqlNested) => {
|
||||
Args: ["-c", command],
|
||||
}
|
||||
: {}),
|
||||
Labels,
|
||||
},
|
||||
Networks: [{ Target: "dokploy-network" }],
|
||||
Networks,
|
||||
RestartPolicy,
|
||||
Placement,
|
||||
Resources: {
|
||||
...resources,
|
||||
},
|
||||
Placement: {
|
||||
Constraints: ["node.role==manager"],
|
||||
},
|
||||
},
|
||||
Mode: {
|
||||
Replicated: {
|
||||
Replicas: 1,
|
||||
},
|
||||
},
|
||||
Mode,
|
||||
RollbackConfig,
|
||||
EndpointSpec: {
|
||||
Mode: "dnsrr",
|
||||
Ports: externalPort
|
||||
@@ -96,6 +106,7 @@ export const buildMysql = async (mysql: MysqlNested) => {
|
||||
]
|
||||
: [],
|
||||
},
|
||||
UpdateConfig,
|
||||
};
|
||||
try {
|
||||
const service = docker.getService(appName);
|
||||
@@ -103,6 +114,10 @@ export const buildMysql = async (mysql: MysqlNested) => {
|
||||
await service.update({
|
||||
version: Number.parseInt(inspect.Version.Index),
|
||||
...settings,
|
||||
TaskTemplate: {
|
||||
...settings.TaskTemplate,
|
||||
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
await docker.createService(settings);
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { CreateServiceOptions } from "dockerode";
|
||||
import {
|
||||
calculateResources,
|
||||
generateBindMounts,
|
||||
generateConfigContainer,
|
||||
generateFileMounts,
|
||||
generateVolumeMounts,
|
||||
prepareEnvironmentVariables,
|
||||
@@ -33,6 +34,17 @@ export const buildPostgres = async (postgres: PostgresNested) => {
|
||||
const defaultPostgresEnv = `POSTGRES_DB="${databaseName}"\nPOSTGRES_USER="${databaseUser}"\nPOSTGRES_PASSWORD="${databasePassword}"${
|
||||
env ? `\n${env}` : ""
|
||||
}`;
|
||||
|
||||
const {
|
||||
HealthCheck,
|
||||
RestartPolicy,
|
||||
Placement,
|
||||
Labels,
|
||||
Mode,
|
||||
RollbackConfig,
|
||||
UpdateConfig,
|
||||
Networks,
|
||||
} = generateConfigContainer(postgres);
|
||||
const resources = calculateResources({
|
||||
memoryLimit,
|
||||
memoryReservation,
|
||||
@@ -53,6 +65,7 @@ export const buildPostgres = async (postgres: PostgresNested) => {
|
||||
Name: appName,
|
||||
TaskTemplate: {
|
||||
ContainerSpec: {
|
||||
HealthCheck,
|
||||
Image: dockerImage,
|
||||
Env: envVariables,
|
||||
Mounts: [...volumesMount, ...bindsMount, ...filesMount],
|
||||
@@ -62,20 +75,17 @@ export const buildPostgres = async (postgres: PostgresNested) => {
|
||||
Args: ["-c", command],
|
||||
}
|
||||
: {}),
|
||||
Labels,
|
||||
},
|
||||
Networks: [{ Target: "dokploy-network" }],
|
||||
Networks,
|
||||
RestartPolicy,
|
||||
Placement,
|
||||
Resources: {
|
||||
...resources,
|
||||
},
|
||||
Placement: {
|
||||
Constraints: ["node.role==manager"],
|
||||
},
|
||||
},
|
||||
Mode: {
|
||||
Replicated: {
|
||||
Replicas: 1,
|
||||
},
|
||||
},
|
||||
Mode,
|
||||
RollbackConfig,
|
||||
EndpointSpec: {
|
||||
Mode: "dnsrr",
|
||||
Ports: externalPort
|
||||
@@ -89,6 +99,7 @@ export const buildPostgres = async (postgres: PostgresNested) => {
|
||||
]
|
||||
: [],
|
||||
},
|
||||
UpdateConfig,
|
||||
};
|
||||
try {
|
||||
const service = docker.getService(appName);
|
||||
@@ -96,6 +107,10 @@ export const buildPostgres = async (postgres: PostgresNested) => {
|
||||
await service.update({
|
||||
version: Number.parseInt(inspect.Version.Index),
|
||||
...settings,
|
||||
TaskTemplate: {
|
||||
...settings.TaskTemplate,
|
||||
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { CreateServiceOptions } from "dockerode";
|
||||
import {
|
||||
calculateResources,
|
||||
generateBindMounts,
|
||||
generateConfigContainer,
|
||||
generateFileMounts,
|
||||
generateVolumeMounts,
|
||||
prepareEnvironmentVariables,
|
||||
@@ -31,6 +32,17 @@ export const buildRedis = async (redis: RedisNested) => {
|
||||
const defaultRedisEnv = `REDIS_PASSWORD="${databasePassword}"${
|
||||
env ? `\n${env}` : ""
|
||||
}`;
|
||||
|
||||
const {
|
||||
HealthCheck,
|
||||
RestartPolicy,
|
||||
Placement,
|
||||
Labels,
|
||||
Mode,
|
||||
RollbackConfig,
|
||||
UpdateConfig,
|
||||
Networks,
|
||||
} = generateConfigContainer(redis);
|
||||
const resources = calculateResources({
|
||||
memoryLimit,
|
||||
memoryReservation,
|
||||
@@ -51,6 +63,7 @@ export const buildRedis = async (redis: RedisNested) => {
|
||||
Name: appName,
|
||||
TaskTemplate: {
|
||||
ContainerSpec: {
|
||||
HealthCheck,
|
||||
Image: dockerImage,
|
||||
Env: envVariables,
|
||||
Mounts: [...volumesMount, ...bindsMount, ...filesMount],
|
||||
@@ -59,20 +72,17 @@ export const buildRedis = async (redis: RedisNested) => {
|
||||
"-c",
|
||||
command ? command : `redis-server --requirepass ${databasePassword}`,
|
||||
],
|
||||
Labels,
|
||||
},
|
||||
Networks: [{ Target: "dokploy-network" }],
|
||||
Networks,
|
||||
RestartPolicy,
|
||||
Placement,
|
||||
Resources: {
|
||||
...resources,
|
||||
},
|
||||
Placement: {
|
||||
Constraints: ["node.role==manager"],
|
||||
},
|
||||
},
|
||||
Mode: {
|
||||
Replicated: {
|
||||
Replicas: 1,
|
||||
},
|
||||
},
|
||||
Mode,
|
||||
RollbackConfig,
|
||||
EndpointSpec: {
|
||||
Mode: "dnsrr",
|
||||
Ports: externalPort
|
||||
@@ -86,6 +96,7 @@ export const buildRedis = async (redis: RedisNested) => {
|
||||
]
|
||||
: [],
|
||||
},
|
||||
UpdateConfig,
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -94,6 +105,10 @@ export const buildRedis = async (redis: RedisNested) => {
|
||||
await service.update({
|
||||
version: Number.parseInt(inspect.Version.Index),
|
||||
...settings,
|
||||
TaskTemplate: {
|
||||
...settings.TaskTemplate,
|
||||
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
await docker.createService(settings);
|
||||
|
||||
@@ -348,7 +348,9 @@ export const calculateResources = ({
|
||||
};
|
||||
};
|
||||
|
||||
export const generateConfigContainer = (application: ApplicationNested) => {
|
||||
export const generateConfigContainer = (
|
||||
application: Partial<ApplicationNested>,
|
||||
) => {
|
||||
const {
|
||||
healthCheckSwarm,
|
||||
restartPolicySwarm,
|
||||
@@ -363,7 +365,7 @@ export const generateConfigContainer = (application: ApplicationNested) => {
|
||||
stopGracePeriodSwarm,
|
||||
} = application;
|
||||
|
||||
const haveMounts = mounts.length > 0;
|
||||
const haveMounts = mounts && mounts.length > 0;
|
||||
|
||||
return {
|
||||
...(healthCheckSwarm && {
|
||||
|
||||
@@ -3,8 +3,8 @@ import { join } from "node:path";
|
||||
import { paths } from "@dokploy/server/constants";
|
||||
import type { Compose } from "@dokploy/server/services/compose";
|
||||
import {
|
||||
type Gitea,
|
||||
findGiteaById,
|
||||
type Gitea,
|
||||
updateGitea,
|
||||
} from "@dokploy/server/services/gitea";
|
||||
import type { InferResultType } from "@dokploy/server/types/with";
|
||||
@@ -118,7 +118,6 @@ export const getGiteaCloneCommand = async (
|
||||
giteaOwner,
|
||||
giteaRepository,
|
||||
serverId,
|
||||
gitea,
|
||||
enableSubmodules,
|
||||
} = entity;
|
||||
|
||||
@@ -145,6 +144,7 @@ export const getGiteaCloneCommand = async (
|
||||
// Use paths(true) for remote operations
|
||||
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
||||
await refreshGiteaToken(giteaId);
|
||||
const gitea = await findGiteaById(giteaId);
|
||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||
const outputPath = join(basePath, appName, "code");
|
||||
|
||||
|
||||
@@ -112,7 +112,6 @@ export const cloneGitlabRepository = async (
|
||||
appName,
|
||||
gitlabBranch,
|
||||
gitlabId,
|
||||
gitlab,
|
||||
gitlabPathNamespace,
|
||||
enableSubmodules,
|
||||
} = entity;
|
||||
@@ -125,6 +124,7 @@ export const cloneGitlabRepository = async (
|
||||
}
|
||||
|
||||
await refreshGitlabToken(gitlabId);
|
||||
const gitlab = await findGitlabById(gitlabId);
|
||||
|
||||
const requirements = getErrorCloneRequirements(entity);
|
||||
|
||||
@@ -187,7 +187,6 @@ export const getGitlabCloneCommand = async (
|
||||
gitlabBranch,
|
||||
gitlabId,
|
||||
serverId,
|
||||
gitlab,
|
||||
enableSubmodules,
|
||||
} = entity;
|
||||
|
||||
@@ -235,6 +234,7 @@ export const getGitlabCloneCommand = async (
|
||||
|
||||
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
||||
await refreshGitlabToken(gitlabId);
|
||||
const gitlab = await findGitlabById(gitlabId);
|
||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||
const outputPath = join(basePath, appName, "code");
|
||||
await recreateDirectory(outputPath);
|
||||
@@ -371,9 +371,9 @@ export const cloneRawGitlabRepository = async (entity: Compose) => {
|
||||
});
|
||||
}
|
||||
|
||||
const gitlabProvider = await findGitlabById(gitlabId);
|
||||
const { COMPOSE_PATH } = paths();
|
||||
await refreshGitlabToken(gitlabId);
|
||||
const gitlabProvider = await findGitlabById(gitlabId);
|
||||
const basePath = COMPOSE_PATH;
|
||||
const outputPath = join(basePath, appName, "code");
|
||||
await recreateDirectory(outputPath);
|
||||
@@ -419,9 +419,9 @@ export const cloneRawGitlabRepositoryRemote = async (compose: Compose) => {
|
||||
message: "Gitlab Provider not found",
|
||||
});
|
||||
}
|
||||
const gitlabProvider = await findGitlabById(gitlabId);
|
||||
const { COMPOSE_PATH } = paths(true);
|
||||
await refreshGitlabToken(gitlabId);
|
||||
const gitlabProvider = await findGitlabById(gitlabId);
|
||||
const basePath = COMPOSE_PATH;
|
||||
const outputPath = join(basePath, appName, "code");
|
||||
const repoClone = getGitlabRepoClone(gitlabProvider, gitlabPathNamespace);
|
||||
|
||||
Reference in New Issue
Block a user