mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-07-10 08:25:22 +02:00
Merge branch 'canary' into feat/ntfy
This commit is contained in:
@@ -32,7 +32,7 @@ export const aiRelations = relations(ai, ({ one }) => ({
|
||||
const createSchema = createInsertSchema(ai, {
|
||||
name: z.string().min(1, { message: "Name is required" }),
|
||||
apiUrl: z.string().url({ message: "Please enter a valid URL" }),
|
||||
apiKey: z.string().min(1, { message: "API Key is required" }),
|
||||
apiKey: z.string(),
|
||||
model: z.string().min(1, { message: "Model is required" }),
|
||||
isEnabled: z.boolean().optional(),
|
||||
});
|
||||
|
||||
@@ -79,6 +79,7 @@ export const applications = pgTable("application", {
|
||||
previewEnv: text("previewEnv"),
|
||||
watchPaths: text("watchPaths").array(),
|
||||
previewBuildArgs: text("previewBuildArgs"),
|
||||
previewLabels: text("previewLabels").array(),
|
||||
previewWildcard: text("previewWildcard"),
|
||||
previewPort: integer("previewPort").default(3000),
|
||||
previewHttps: boolean("previewHttps").notNull().default(false),
|
||||
@@ -308,6 +309,7 @@ const createSchema = createInsertSchema(applications, {
|
||||
previewCertificateType: z.enum(["letsencrypt", "none", "custom"]).optional(),
|
||||
previewRequireCollaboratorPermissions: z.boolean().optional(),
|
||||
watchPaths: z.array(z.string()).optional(),
|
||||
previewLabels: z.array(z.string()).optional(),
|
||||
cleanCache: z.boolean().optional(),
|
||||
});
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ export const findApplicationByPreview = async (applicationId: string) => {
|
||||
if (!application) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Applicationnot found",
|
||||
message: "Application not found",
|
||||
});
|
||||
}
|
||||
return application;
|
||||
@@ -78,35 +78,41 @@ export const findApplicationByPreview = async (applicationId: string) => {
|
||||
|
||||
export const removePreviewDeployment = async (previewDeploymentId: string) => {
|
||||
try {
|
||||
const application = await findApplicationByPreview(previewDeploymentId);
|
||||
const previewDeployment =
|
||||
await findPreviewDeploymentById(previewDeploymentId);
|
||||
|
||||
const deployment = await db
|
||||
.delete(previewDeployments)
|
||||
.where(eq(previewDeployments.previewDeploymentId, previewDeploymentId))
|
||||
.returning();
|
||||
const application = await findApplicationById(
|
||||
previewDeployment.applicationId,
|
||||
);
|
||||
|
||||
application.appName = previewDeployment.appName;
|
||||
const cleanupOperations = [
|
||||
async () =>
|
||||
await removeService(application?.appName, application?.serverId),
|
||||
async () =>
|
||||
await removeDeploymentsByPreviewDeploymentId(
|
||||
previewDeployment,
|
||||
application.serverId,
|
||||
application?.serverId,
|
||||
),
|
||||
async () =>
|
||||
await removeDirectoryCode(application.appName, application.serverId),
|
||||
await removeDirectoryCode(application?.appName, application?.serverId),
|
||||
async () =>
|
||||
await removeTraefikConfig(application.appName, application.serverId),
|
||||
await removeTraefikConfig(application?.appName, application?.serverId),
|
||||
async () =>
|
||||
await removeService(application?.appName, application.serverId),
|
||||
await db
|
||||
.delete(previewDeployments)
|
||||
.where(
|
||||
eq(previewDeployments.previewDeploymentId, previewDeploymentId),
|
||||
)
|
||||
.returning(),
|
||||
];
|
||||
for (const operation of cleanupOperations) {
|
||||
try {
|
||||
await operation();
|
||||
} catch {}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return deployment[0];
|
||||
return previewDeployment;
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error
|
||||
|
||||
@@ -13,7 +13,7 @@ export const TRAEFIK_PORT =
|
||||
Number.parseInt(process.env.TRAEFIK_PORT!, 10) || 80;
|
||||
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";
|
||||
export const TRAEFIK_VERSION = process.env.TRAEFIK_VERSION || "3.5.0";
|
||||
|
||||
export interface TraefikOptions {
|
||||
env?: string[];
|
||||
|
||||
@@ -5,17 +5,16 @@ import { createDeepInfra } from "@ai-sdk/deepinfra";
|
||||
import { createMistral } from "@ai-sdk/mistral";
|
||||
import { createOpenAI } from "@ai-sdk/openai";
|
||||
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
||||
import { createOllama } from "ollama-ai-provider";
|
||||
import { createOllama } from "ai-sdk-ollama";
|
||||
|
||||
function getProviderName(apiUrl: string) {
|
||||
export function getProviderName(apiUrl: string) {
|
||||
if (apiUrl.includes("api.openai.com")) return "openai";
|
||||
if (apiUrl.includes("azure.com")) return "azure";
|
||||
if (apiUrl.includes("api.anthropic.com")) return "anthropic";
|
||||
if (apiUrl.includes("api.cohere.ai")) return "cohere";
|
||||
if (apiUrl.includes("api.perplexity.ai")) return "perplexity";
|
||||
if (apiUrl.includes("api.mistral.ai")) return "mistral";
|
||||
if (apiUrl.includes("localhost:11434") || apiUrl.includes("ollama"))
|
||||
return "ollama";
|
||||
if (apiUrl.includes(":11434") || apiUrl.includes("ollama")) return "ollama";
|
||||
if (apiUrl.includes("api.deepinfra.com")) return "deepinfra";
|
||||
return "custom";
|
||||
}
|
||||
|
||||
@@ -254,6 +254,9 @@ export const addDomainToCompose = async (
|
||||
if (!labels.includes("traefik.docker.network=dokploy-network")) {
|
||||
labels.unshift("traefik.docker.network=dokploy-network");
|
||||
}
|
||||
if (!labels.includes("traefik.swarm.network=dokploy-network")) {
|
||||
labels.unshift("traefik.swarm.network=dokploy-network");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -273,6 +273,14 @@ export const prepareEnvironmentVariables = (
|
||||
throw new Error(`Invalid project environment variable: project.${ref}`);
|
||||
});
|
||||
}
|
||||
|
||||
resolvedValue = resolvedValue.replace(/\$\{\{(.*?)\}\}/g, (_, ref) => {
|
||||
if (serviceVars[ref] !== undefined) {
|
||||
return serviceVars[ref];
|
||||
}
|
||||
throw new Error(`Invalid service environment variable: ${ref}`);
|
||||
});
|
||||
|
||||
return `${key}=${resolvedValue}`;
|
||||
});
|
||||
|
||||
|
||||
@@ -2,11 +2,14 @@ import { findVolumeBackupById } from "@dokploy/server/services/volume-backups";
|
||||
import { scheduledJobs, scheduleJob } from "node-schedule";
|
||||
import {
|
||||
createDeploymentVolumeBackup,
|
||||
updateDeploymentStatus,
|
||||
} from "@dokploy/server/services/deployment";
|
||||
import {
|
||||
execAsync,
|
||||
execAsyncRemote,
|
||||
updateDeploymentStatus,
|
||||
} from "../..";
|
||||
} from "@dokploy/server/utils/process/execAsync";
|
||||
import { backupVolume } from "./backup";
|
||||
import { getS3Credentials, normalizeS3Path } from "../backups/utils";
|
||||
|
||||
export const scheduleVolumeBackup = async (volumeBackupId: string) => {
|
||||
const volumeBackup = await findVolumeBackupById(volumeBackupId);
|
||||
@@ -20,6 +23,33 @@ export const removeVolumeBackupJob = async (volumeBackupId: string) => {
|
||||
currentJob?.cancel();
|
||||
};
|
||||
|
||||
const cleanupOldVolumeBackups = async (
|
||||
volumeBackup: Awaited<ReturnType<typeof findVolumeBackupById>>,
|
||||
serverId?: string | null,
|
||||
) => {
|
||||
const { keepLatestCount, destination, prefix, volumeName } = volumeBackup;
|
||||
|
||||
if (!keepLatestCount) return;
|
||||
|
||||
try {
|
||||
const rcloneFlags = getS3Credentials(destination);
|
||||
const normalizedPrefix = normalizeS3Path(prefix);
|
||||
const backupFilesPath = `:s3:${destination.bucket}/${normalizedPrefix}`;
|
||||
const listCommand = `rclone lsf ${rcloneFlags.join(" ")} --include \"${volumeName}-*.tar\" :s3:${destination.bucket}/${normalizedPrefix}`;
|
||||
const sortAndPick = `sort -r | tail -n +$((${keepLatestCount}+1)) | xargs -I{}`;
|
||||
const deleteCommand = `rclone delete ${rcloneFlags.join(" ")} ${backupFilesPath}{}`;
|
||||
const fullCommand = `${listCommand} | ${sortAndPick} ${deleteCommand}`;
|
||||
|
||||
if (serverId) {
|
||||
await execAsyncRemote(serverId, fullCommand);
|
||||
} else {
|
||||
await execAsync(fullCommand);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Volume backup retention error", error);
|
||||
}
|
||||
};
|
||||
|
||||
export const runVolumeBackup = async (volumeBackupId: string) => {
|
||||
const volumeBackup = await findVolumeBackupById(volumeBackupId);
|
||||
const serverId =
|
||||
@@ -40,6 +70,10 @@ export const runVolumeBackup = async (volumeBackupId: string) => {
|
||||
await execAsync(commandWithLog);
|
||||
}
|
||||
|
||||
if (volumeBackup.keepLatestCount && volumeBackup.keepLatestCount > 0) {
|
||||
await cleanupOldVolumeBackups(volumeBackup, serverId);
|
||||
}
|
||||
|
||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
||||
} catch (error) {
|
||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
||||
|
||||
Reference in New Issue
Block a user