Skip to content

Commit 879218a

Browse files
committed
Merge branch 'canary' into style/deployments-remove-max-w-8xl
2 parents 4ef8c94 + d6124aa commit 879218a

340 files changed

Lines changed: 181632 additions & 23914 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/pr-quality.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ jobs:
1616
steps:
1717
- uses: peakoss/anti-slop@v0
1818
with:
19-
max-failures: 4
2019
blocked-commit-authors: "claude,copilot"
2120
require-description: true
2221
min-account-age: 5

CONTRIBUTING.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,14 @@ pnpm run dokploy:build
9999

100100
## Docker
101101

102-
To build the docker image
102+
To build the docker image first run commands to copy .env files
103+
104+
```bash
105+
cp apps/dokploy/.env.production.example .env.production
106+
cp apps/dokploy/.env.production.example apps/dokploy/.env.production
107+
```
108+
109+
then run build command
103110

104111
```bash
105112
pnpm run docker:build

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Dokploy is a free, self-hostable Platform as a Service (PaaS) that simplifies th
1919
Dokploy includes multiple features to make your life easier.
2020

2121
- **Applications**: Deploy any type of application (Node.js, PHP, Python, Go, Ruby, etc.).
22-
- **Databases**: Create and manage databases with support for MySQL, PostgreSQL, MongoDB, MariaDB, and Redis.
22+
- **Databases**: Create and manage databases with support for MySQL, PostgreSQL, MongoDB, MariaDB, libsql, and Redis.
2323
- **Backups**: Automate backups for databases to an external storage destination.
2424
- **Docker Compose**: Native support for Docker Compose to manage complex applications.
2525
- **Multi Node**: Scale applications to multiple nodes using Docker Swarm to manage the cluster.
@@ -39,7 +39,7 @@ To get started, run the following command on a VPS:
3939
Want to skip the installation process? [Try the Dokploy Cloud](https://app.dokploy.com).
4040

4141
```bash
42-
curl -sSL https://dokploy.com/install.sh | sh
42+
curl -sSL https://dokploy.com/install.sh | bash
4343
```
4444

4545
For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).

apps/dokploy/__test__/compose/domain/host-rule-format.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ describe("Host rule format regression tests", () => {
3232
previewDeploymentId: "",
3333
internalPath: "/",
3434
stripPath: false,
35+
customEntrypoint: null,
36+
middlewares: null,
3537
};
3638

3739
describe("Host rule format validation", () => {

apps/dokploy/__test__/compose/domain/labels.test.ts

Lines changed: 262 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ describe("createDomainLabels", () => {
77
const baseDomain: Domain = {
88
host: "example.com",
99
port: 8080,
10+
customEntrypoint: null,
1011
https: false,
1112
uniqueConfigKey: 1,
1213
customCertResolver: null,
@@ -21,6 +22,7 @@ describe("createDomainLabels", () => {
2122
previewDeploymentId: "",
2223
internalPath: "/",
2324
stripPath: false,
25+
middlewares: null,
2426
};
2527

2628
it("should create basic labels for web entrypoint", async () => {
@@ -171,12 +173,12 @@ describe("createDomainLabels", () => {
171173
"websecure",
172174
);
173175

174-
// Web entrypoint should have both middlewares with redirect first
176+
// Web entrypoint with HTTPS should only have redirect
175177
expect(webLabels).toContain(
176-
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file,addprefix-test-app-1",
178+
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
177179
);
178180

179-
// Websecure should only have the addprefix middleware
181+
// Websecure should have the addprefix middleware
180182
expect(websecureLabels).toContain(
181183
"traefik.http.routers.test-app-1-websecure.middlewares=addprefix-test-app-1",
182184
);
@@ -208,9 +210,9 @@ describe("createDomainLabels", () => {
208210
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
209211
);
210212

211-
// Should have middlewares in correct order: redirect, stripprefix, addprefix
213+
// Web router with HTTPS should only have redirect
212214
expect(webLabels).toContain(
213-
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file,stripprefix-test-app-1,addprefix-test-app-1",
215+
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
214216
);
215217
});
216218

@@ -240,4 +242,259 @@ describe("createDomainLabels", () => {
240242
"traefik.http.routers.test-app-1-websecure.middlewares=stripprefix-test-app-1,addprefix-test-app-1",
241243
);
242244
});
245+
246+
it("should add single custom middleware to router", async () => {
247+
const customMiddlewareDomain = {
248+
...baseDomain,
249+
middlewares: ["auth@file"],
250+
};
251+
const labels = await createDomainLabels(
252+
appName,
253+
customMiddlewareDomain,
254+
"web",
255+
);
256+
257+
expect(labels).toContain(
258+
"traefik.http.routers.test-app-1-web.middlewares=auth@file",
259+
);
260+
});
261+
262+
it("should add multiple custom middlewares to router", async () => {
263+
const customMiddlewareDomain = {
264+
...baseDomain,
265+
middlewares: ["auth@file", "rate-limit@file"],
266+
};
267+
const labels = await createDomainLabels(
268+
appName,
269+
customMiddlewareDomain,
270+
"web",
271+
);
272+
273+
expect(labels).toContain(
274+
"traefik.http.routers.test-app-1-web.middlewares=auth@file,rate-limit@file",
275+
);
276+
});
277+
278+
it("should only have redirect on web router when HTTPS is enabled with custom middlewares", async () => {
279+
const combinedDomain = {
280+
...baseDomain,
281+
https: true,
282+
middlewares: ["auth@file"],
283+
};
284+
const labels = await createDomainLabels(appName, combinedDomain, "web");
285+
286+
// Web router with HTTPS should only redirect, custom middlewares go on websecure
287+
expect(labels).toContain(
288+
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
289+
);
290+
expect(labels).not.toContain("auth@file");
291+
});
292+
293+
it("should combine custom middlewares with stripPath middleware (no HTTPS)", async () => {
294+
const combinedDomain = {
295+
...baseDomain,
296+
path: "/api",
297+
stripPath: true,
298+
middlewares: ["auth@file"],
299+
};
300+
const labels = await createDomainLabels(appName, combinedDomain, "web");
301+
302+
// stripprefix should come before custom middleware
303+
expect(labels).toContain(
304+
"traefik.http.routers.test-app-1-web.middlewares=stripprefix-test-app-1,auth@file",
305+
);
306+
});
307+
308+
it("should only have redirect on web router even with all built-in middlewares and custom middlewares", async () => {
309+
const fullDomain = {
310+
...baseDomain,
311+
https: true,
312+
path: "/api",
313+
stripPath: true,
314+
internalPath: "/hello",
315+
middlewares: ["auth@file", "rate-limit@file"],
316+
};
317+
const webLabels = await createDomainLabels(appName, fullDomain, "web");
318+
319+
// Web router with HTTPS should only redirect
320+
expect(webLabels).toContain(
321+
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
322+
);
323+
// Middleware definitions should still be present (Traefik needs them registered)
324+
expect(webLabels).toContain(
325+
"traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api",
326+
);
327+
expect(webLabels).toContain(
328+
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
329+
);
330+
// But they should NOT be attached to the router
331+
expect(webLabels).not.toContain("stripprefix-test-app-1,");
332+
expect(webLabels).not.toContain("auth@file");
333+
expect(webLabels).not.toContain("rate-limit@file");
334+
});
335+
336+
it("should include custom middlewares on websecure entrypoint", async () => {
337+
const customMiddlewareDomain = {
338+
...baseDomain,
339+
https: true,
340+
middlewares: ["auth@file"],
341+
};
342+
const websecureLabels = await createDomainLabels(
343+
appName,
344+
customMiddlewareDomain,
345+
"websecure",
346+
);
347+
348+
// Websecure should have custom middleware but not redirect-to-https
349+
expect(websecureLabels).toContain(
350+
"traefik.http.routers.test-app-1-websecure.middlewares=auth@file",
351+
);
352+
expect(websecureLabels).not.toContain("redirect-to-https");
353+
});
354+
355+
it("should NOT include custom middlewares on web router when HTTPS is enabled (only redirect)", async () => {
356+
const domain = {
357+
...baseDomain,
358+
https: true,
359+
middlewares: ["rate-limit@file", "auth@file"],
360+
};
361+
const webLabels = await createDomainLabels(appName, domain, "web");
362+
363+
// Web router with HTTPS should ONLY have redirect, not custom middlewares
364+
expect(webLabels).toContain(
365+
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
366+
);
367+
expect(webLabels).not.toContain("rate-limit@file");
368+
expect(webLabels).not.toContain("auth@file");
369+
});
370+
371+
it("should create basic labels for custom entrypoint", async () => {
372+
const labels = await createDomainLabels(
373+
appName,
374+
{ ...baseDomain, customEntrypoint: "custom" },
375+
"custom",
376+
);
377+
expect(labels).toEqual([
378+
"traefik.http.routers.test-app-1-custom.rule=Host(`example.com`)",
379+
"traefik.http.routers.test-app-1-custom.entrypoints=custom",
380+
"traefik.http.services.test-app-1-custom.loadbalancer.server.port=8080",
381+
"traefik.http.routers.test-app-1-custom.service=test-app-1-custom",
382+
]);
383+
});
384+
385+
it("should create https labels for custom entrypoint", async () => {
386+
const labels = await createDomainLabels(
387+
appName,
388+
{
389+
...baseDomain,
390+
https: true,
391+
customEntrypoint: "custom",
392+
certificateType: "letsencrypt",
393+
},
394+
"custom",
395+
);
396+
expect(labels).toEqual([
397+
"traefik.http.routers.test-app-1-custom.rule=Host(`example.com`)",
398+
"traefik.http.routers.test-app-1-custom.entrypoints=custom",
399+
"traefik.http.services.test-app-1-custom.loadbalancer.server.port=8080",
400+
"traefik.http.routers.test-app-1-custom.service=test-app-1-custom",
401+
"traefik.http.routers.test-app-1-custom.tls.certresolver=letsencrypt",
402+
]);
403+
});
404+
405+
it("should add stripPath middleware for custom entrypoint", async () => {
406+
const labels = await createDomainLabels(
407+
appName,
408+
{
409+
...baseDomain,
410+
customEntrypoint: "custom",
411+
path: "/api",
412+
stripPath: true,
413+
},
414+
"custom",
415+
);
416+
417+
expect(labels).toContain(
418+
"traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api",
419+
);
420+
expect(labels).toContain(
421+
"traefik.http.routers.test-app-1-custom.middlewares=stripprefix-test-app-1",
422+
);
423+
});
424+
425+
it("should add internalPath middleware for custom entrypoint", async () => {
426+
const labels = await createDomainLabels(
427+
appName,
428+
{
429+
...baseDomain,
430+
customEntrypoint: "custom",
431+
internalPath: "/hello",
432+
},
433+
"custom",
434+
);
435+
436+
expect(labels).toContain(
437+
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
438+
);
439+
expect(labels).toContain(
440+
"traefik.http.routers.test-app-1-custom.middlewares=addprefix-test-app-1",
441+
);
442+
});
443+
444+
it("should add path prefix in rule for custom entrypoint", async () => {
445+
const labels = await createDomainLabels(
446+
appName,
447+
{
448+
...baseDomain,
449+
customEntrypoint: "custom",
450+
path: "/api",
451+
},
452+
"custom",
453+
);
454+
455+
expect(labels).toContain(
456+
"traefik.http.routers.test-app-1-custom.rule=Host(`example.com`) && PathPrefix(`/api`)",
457+
);
458+
});
459+
460+
it("should combine all middlewares for custom entrypoint", async () => {
461+
const labels = await createDomainLabels(
462+
appName,
463+
{
464+
...baseDomain,
465+
customEntrypoint: "custom",
466+
path: "/api",
467+
stripPath: true,
468+
internalPath: "/hello",
469+
},
470+
"custom",
471+
);
472+
473+
expect(labels).toContain(
474+
"traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api",
475+
);
476+
expect(labels).toContain(
477+
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
478+
);
479+
expect(labels).toContain(
480+
"traefik.http.routers.test-app-1-custom.middlewares=stripprefix-test-app-1,addprefix-test-app-1",
481+
);
482+
});
483+
484+
it("should not add redirect-to-https for custom entrypoint even with https", async () => {
485+
const labels = await createDomainLabels(
486+
appName,
487+
{
488+
...baseDomain,
489+
customEntrypoint: "custom",
490+
https: true,
491+
certificateType: "letsencrypt",
492+
},
493+
"custom",
494+
);
495+
496+
const middlewareLabel = labels.find((l) => l.includes(".middlewares="));
497+
// Should not contain redirect-to-https since there's only one router
498+
expect(middlewareLabel).toBeUndefined();
499+
});
243500
});

apps/dokploy/__test__/compose/network/network-root.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ networks:
292292
dokploy-network:
293293
`;
294294

295-
test("It shoudn't add suffix to dokploy-network", () => {
295+
test("It shouldn't add suffix to dokploy-network", () => {
296296
const composeData = parse(composeFile7) as ComposeSpecification;
297297

298298
const suffix = generateRandomHash();

apps/dokploy/__test__/compose/network/network-service.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ services:
195195
- dokploy-network
196196
`;
197197

198-
test("It shoudn't add suffix to dokploy-network in services", () => {
198+
test("It shouldn't add suffix to dokploy-network in services", () => {
199199
const composeData = parse(composeFile7) as ComposeSpecification;
200200

201201
const suffix = generateRandomHash();
@@ -241,10 +241,10 @@ services:
241241
dokploy-network:
242242
aliases:
243243
- apid
244-
244+
245245
`;
246246

247-
test("It shoudn't add suffix to dokploy-network in services multiples cases", () => {
247+
test("It shouldn't add suffix to dokploy-network in services multiples cases", () => {
248248
const composeData = parse(composeFile8) as ComposeSpecification;
249249

250250
const suffix = generateRandomHash();

apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts renamed to apps/dokploy/__test__/compose/service/service-volumes-from.test.ts

File renamed without changes.

0 commit comments

Comments
 (0)