mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-29 19:15:22 +02:00
Compare commits
6 Commits
feature/ma
...
v0.29.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f43f605f3 | ||
|
|
103e2f70a8 | ||
|
|
34d38cf90e | ||
|
|
f6e6e5cc00 | ||
|
|
b06138b230 | ||
|
|
af8072d7ad |
@@ -103,6 +103,51 @@ describe("createDomainLabels", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should add tls=true for certificateType none on websecure entrypoint", async () => {
|
||||||
|
const noneDomain = {
|
||||||
|
...baseDomain,
|
||||||
|
https: true,
|
||||||
|
certificateType: "none" as const,
|
||||||
|
};
|
||||||
|
const labels = await createDomainLabels(appName, noneDomain, "websecure");
|
||||||
|
expect(labels).toContain(
|
||||||
|
"traefik.http.routers.test-app-1-websecure.tls=true",
|
||||||
|
);
|
||||||
|
// no cert resolver should be set when relying on a default/custom cert
|
||||||
|
expect(labels).not.toContain(
|
||||||
|
"traefik.http.routers.test-app-1-websecure.tls.certresolver=letsencrypt",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not add tls=true for certificateType none on web entrypoint", async () => {
|
||||||
|
const noneDomain = {
|
||||||
|
...baseDomain,
|
||||||
|
https: true,
|
||||||
|
certificateType: "none" as const,
|
||||||
|
};
|
||||||
|
const labels = await createDomainLabels(appName, noneDomain, "web");
|
||||||
|
expect(labels).not.toContain(
|
||||||
|
"traefik.http.routers.test-app-1-web.tls=true",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add tls=true for certificateType none on a custom https entrypoint", async () => {
|
||||||
|
const noneDomain = {
|
||||||
|
...baseDomain,
|
||||||
|
https: true,
|
||||||
|
customEntrypoint: "websecure-custom",
|
||||||
|
certificateType: "none" as const,
|
||||||
|
};
|
||||||
|
const labels = await createDomainLabels(
|
||||||
|
appName,
|
||||||
|
noneDomain,
|
||||||
|
"websecure-custom",
|
||||||
|
);
|
||||||
|
expect(labels).toContain(
|
||||||
|
"traefik.http.routers.test-app-1-websecure-custom.tls=true",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("should handle different ports correctly", async () => {
|
it("should handle different ports correctly", async () => {
|
||||||
const customPortDomain = { ...baseDomain, port: 3000 };
|
const customPortDomain = { ...baseDomain, port: 3000 };
|
||||||
const labels = await createDomainLabels(appName, customPortDomain, "web");
|
const labels = await createDomainLabels(appName, customPortDomain, "web");
|
||||||
|
|||||||
41
apps/dokploy/__test__/deploy/should-deploy.test.ts
Normal file
41
apps/dokploy/__test__/deploy/should-deploy.test.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { shouldDeploy } from "@dokploy/server";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
describe("shouldDeploy", () => {
|
||||||
|
it("should deploy when no watch paths are configured", () => {
|
||||||
|
expect(shouldDeploy(null, ["src/index.ts"])).toBe(true);
|
||||||
|
expect(shouldDeploy([], ["src/index.ts"])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deploy when watch paths match modified files", () => {
|
||||||
|
expect(shouldDeploy(["src/**"], ["src/index.ts"])).toBe(true);
|
||||||
|
expect(shouldDeploy(["apps/web/**"], ["apps/web/page.tsx"])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not deploy when watch paths do not match", () => {
|
||||||
|
expect(shouldDeploy(["src/**"], ["docs/readme.md"])).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not throw when modified files contain non-string values", () => {
|
||||||
|
expect(() =>
|
||||||
|
shouldDeploy(["src/**"], ["src/index.ts", undefined, null] as any),
|
||||||
|
).not.toThrow();
|
||||||
|
expect(
|
||||||
|
shouldDeploy(["src/**"], ["src/index.ts", undefined, null] as any),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not throw when modified files are undefined or null", () => {
|
||||||
|
expect(() => shouldDeploy(["src/**"], undefined)).not.toThrow();
|
||||||
|
expect(() => shouldDeploy(["src/**"], null)).not.toThrow();
|
||||||
|
expect(shouldDeploy(["src/**"], undefined)).toBe(false);
|
||||||
|
expect(shouldDeploy(["src/**"], null)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not throw when every modified file is non-string", () => {
|
||||||
|
expect(() =>
|
||||||
|
shouldDeploy(["src/**"], [undefined, undefined] as any),
|
||||||
|
).not.toThrow();
|
||||||
|
expect(shouldDeploy(["src/**"], [undefined, undefined] as any)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -78,4 +78,20 @@ describe("readValidDirectory (path traversal)", () => {
|
|||||||
it("returns false for empty string (resolves to cwd)", () => {
|
it("returns false for empty string (resolves to cwd)", () => {
|
||||||
expect(readValidDirectory("")).toBe(false);
|
expect(readValidDirectory("")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns true for Next.js dynamic route paths with square brackets", () => {
|
||||||
|
expect(
|
||||||
|
readValidDirectory(
|
||||||
|
`${BASE}/applications/myapp/code/app/api/[id]/route.ts`,
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
readValidDirectory(`${BASE}/applications/myapp/code/pages/[slug].tsx`),
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
readValidDirectory(
|
||||||
|
`${BASE}/applications/myapp/code/app/[...catch]/page.tsx`,
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ export const ShowResources = ({ id, type }: Props) => {
|
|||||||
<FormLabel>Memory Limit</FormLabel>
|
<FormLabel>Memory Limit</FormLabel>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip delayDuration={0}>
|
<Tooltip delayDuration={0}>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger type="button">
|
||||||
<InfoIcon className="h-4 w-4 text-muted-foreground" />
|
<InfoIcon className="h-4 w-4 text-muted-foreground" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
@@ -263,7 +263,7 @@ export const ShowResources = ({ id, type }: Props) => {
|
|||||||
<FormLabel>Memory Reservation</FormLabel>
|
<FormLabel>Memory Reservation</FormLabel>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip delayDuration={0}>
|
<Tooltip delayDuration={0}>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger type="button">
|
||||||
<InfoIcon className="h-4 w-4 text-muted-foreground" />
|
<InfoIcon className="h-4 w-4 text-muted-foreground" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
@@ -303,7 +303,7 @@ export const ShowResources = ({ id, type }: Props) => {
|
|||||||
<FormLabel>CPU Limit</FormLabel>
|
<FormLabel>CPU Limit</FormLabel>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip delayDuration={0}>
|
<Tooltip delayDuration={0}>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger type="button">
|
||||||
<InfoIcon className="h-4 w-4 text-muted-foreground" />
|
<InfoIcon className="h-4 w-4 text-muted-foreground" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
@@ -343,7 +343,7 @@ export const ShowResources = ({ id, type }: Props) => {
|
|||||||
<FormLabel>CPU Reservation</FormLabel>
|
<FormLabel>CPU Reservation</FormLabel>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip delayDuration={0}>
|
<Tooltip delayDuration={0}>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger type="button">
|
||||||
<InfoIcon className="h-4 w-4 text-muted-foreground" />
|
<InfoIcon className="h-4 w-4 text-muted-foreground" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
@@ -379,7 +379,7 @@ export const ShowResources = ({ id, type }: Props) => {
|
|||||||
<FormLabel className="text-base">Ulimits</FormLabel>
|
<FormLabel className="text-base">Ulimits</FormLabel>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip delayDuration={0}>
|
<Tooltip delayDuration={0}>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger type="button">
|
||||||
<InfoIcon className="h-4 w-4 text-muted-foreground" />
|
<InfoIcon className="h-4 w-4 text-muted-foreground" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent className="max-w-xs">
|
<TooltipContent className="max-w-xs">
|
||||||
|
|||||||
@@ -806,7 +806,7 @@ export const AddDomain = ({ id, type, domainId = "", children }: Props) => {
|
|||||||
<FormLabel>Middlewares</FormLabel>
|
<FormLabel>Middlewares</FormLabel>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger type="button">
|
||||||
<div className="size-4 rounded-full bg-muted flex items-center justify-center text-[10px] font-bold">
|
<div className="size-4 rounded-full bg-muted flex items-center justify-center text-[10px] font-bold">
|
||||||
?
|
?
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -422,7 +422,7 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => {
|
|||||||
<FormLabel>Watch Paths</FormLabel>
|
<FormLabel>Watch Paths</FormLabel>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger type="button">
|
||||||
<div className="size-4 rounded-full bg-muted flex items-center justify-center text-[10px] font-bold">
|
<div className="size-4 rounded-full bg-muted flex items-center justify-center text-[10px] font-bold">
|
||||||
?
|
?
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -449,7 +449,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
|||||||
<FormLabel>Watch Paths</FormLabel>
|
<FormLabel>Watch Paths</FormLabel>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger type="button">
|
||||||
<div className="size-4 rounded-full bg-muted flex items-center justify-center text-[10px] font-bold">
|
<div className="size-4 rounded-full bg-muted flex items-center justify-center text-[10px] font-bold">
|
||||||
?
|
?
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -440,7 +440,7 @@ export const SaveGitlabProviderCompose = ({ composeId }: Props) => {
|
|||||||
<FormLabel>Watch Paths</FormLabel>
|
<FormLabel>Watch Paths</FormLabel>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger type="button">
|
||||||
<div className="size-4 rounded-full bg-muted flex items-center justify-center text-[10px] font-bold">
|
<div className="size-4 rounded-full bg-muted flex items-center justify-center text-[10px] font-bold">
|
||||||
?
|
?
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -167,7 +167,13 @@ export const CodeEditor = ({
|
|||||||
? css()
|
? css()
|
||||||
: language === "shell"
|
: language === "shell"
|
||||||
? StreamLanguage.define(shell)
|
? StreamLanguage.define(shell)
|
||||||
: StreamLanguage.define(properties),
|
: StreamLanguage.define({
|
||||||
|
...properties,
|
||||||
|
// The legacy properties mode lacks comment metadata, so
|
||||||
|
// CodeMirror's toggle-comment shortcut (Mod-/) has no comment
|
||||||
|
// token to use. Declare `#` as the line comment for env editors.
|
||||||
|
languageData: { commentTokens: { line: "#" } },
|
||||||
|
}),
|
||||||
props.lineWrapping ? EditorView.lineWrapping : [],
|
props.lineWrapping ? EditorView.lineWrapping : [],
|
||||||
language === "yaml"
|
language === "yaml"
|
||||||
? autocompletion({
|
? autocompletion({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "dokploy",
|
"name": "dokploy",
|
||||||
"version": "v0.29.4",
|
"version": "v0.29.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -337,6 +337,10 @@ export const createDomainLabels = (
|
|||||||
labels.push(
|
labels.push(
|
||||||
`traefik.http.routers.${routerName}.tls.certresolver=${customCertResolver}`,
|
`traefik.http.routers.${routerName}.tls.certresolver=${customCertResolver}`,
|
||||||
);
|
);
|
||||||
|
} else if (certificateType === "none" && https) {
|
||||||
|
// No cert resolver, but HTTPS is enabled (default/custom certificate):
|
||||||
|
// explicitly enable TLS so Traefik serves the router over HTTPS.
|
||||||
|
labels.push(`traefik.http.routers.${routerName}.tls=true`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import micromatch from "micromatch";
|
|||||||
|
|
||||||
export const shouldDeploy = (
|
export const shouldDeploy = (
|
||||||
watchPaths: string[] | null,
|
watchPaths: string[] | null,
|
||||||
modifiedFiles: string[],
|
modifiedFiles: (string | null | undefined)[] | null | undefined,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if (!watchPaths || watchPaths?.length === 0) return true;
|
if (!watchPaths || watchPaths?.length === 0) return true;
|
||||||
return micromatch.some(modifiedFiles, watchPaths);
|
const files = (modifiedFiles ?? []).filter(
|
||||||
|
(file): file is string => typeof file === "string",
|
||||||
|
);
|
||||||
|
return micromatch.some(files, watchPaths);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const readValidDirectory = (
|
|||||||
directory: string,
|
directory: string,
|
||||||
serverId?: string | null,
|
serverId?: string | null,
|
||||||
) => {
|
) => {
|
||||||
if (!/^[\w/. :-]{1,500}$/.test(directory)) {
|
if (!/^[\w/. :[\]-]{1,500}$/.test(directory)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user