Repository navigation
Conversation
…ices Fixes Dokploy#5616 - Validate collisions matching lower(host), path prefix, and serverId - Support path-based routing so distinct paths on the same host do not conflict - Enforce case-insensitive host matching per RFC 4343 - Run validation within database transaction to prevent concurrent duplicate inserts - Preserve TRPCError instances in domain.create router so CONFLICT code reaches API callers - Add unit test coverage for path routing, case insensitivity, multi-server isolation, and successful creation
| const result = await db.transaction(async (tx) => { | ||
| const conflictingDomain = await findConflictingDomain({ | ||
| host: cleanHost, | ||
| path: input.path, | ||
| serverId, | ||
| }); |
There was a problem hiding this comment.
Concurrent saves still collide
Two requests saving the same host, path, and server can both pass this check before either commits. findConflictingDomain reads through the global db, while the insert uses tx. No lock or unique constraint stops both inserts from succeeding. Updates have the same check-then-save race, so conflicting Traefik routes can still be created.
Use a shared lock or database-enforced rule for creates and updates. Moving the read into tx alone will not prevent duplicates.
| if (conflictingDomain) { | ||
| const targetPath = normalizeDomainPath(input.path); | ||
| const pathDisplay = | ||
| targetPath === "/" ? "" : ` with path '${targetPath}'`; | ||
| throw new TRPCError({ | ||
| code: "CONFLICT", | ||
| message: `Domain '${cleanHost}'${pathDisplay} is already in use on this server`, | ||
| }); |
There was a problem hiding this comment.
This rejection breaks project.duplicate for services with domains. That flow first creates the copied application or Compose service, then calls createDomain with the source host and path on the same server. The source domain now triggers CONFLICT, so duplication fails after earlier writes have committed. The copied project or services remain partly created.
Update duplication to omit or replace conflicting domains, or reject before creating the copied resources.
| ) => { | ||
| const currentDomain = await findDomainById(domainId); | ||
|
|
||
| if (domainData.host !== undefined) { |
There was a problem hiding this comment.
Unchanged settings cannot be saved
A domain with an existing duplicate can no longer receive settings-only edits. The dashboard always sends host, even when the user changes only a port or certificate. This branch checks for conflicts whenever host is supplied. Excluding the current row still finds the other old duplicate and rejects the save, even though the route has not changed.
Compare the old and proposed host/path before checking, so unchanged routes can still have their settings edited.
| host: "app.example.com", | ||
| path: "/", | ||
| application: { serverId: "server-1" }, | ||
| }, | ||
| ]); | ||
|
|
||
| await expect( | ||
| createDomain({ | ||
| host: "APP.EXAMPLE.COM", |
There was a problem hiding this comment.
This test does not check case-insensitive host matching. findMany returns the same canned row regardless of its query, so replacing lower(host) with a case-sensitive comparison would still pass. Add a database-backed test, or assert the generated query, so this test catches that regression.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
|
Pushed
|
Summary
Fixes #5616
In Dokploy, assigning an existing domain and path to another application or compose service caused conflicting Traefik router rules on the same server, resulting in unpredictable routing and intermittent 500 errors.
Changes
packages/server/src/services/domain.ts:findConflictingDomainchecking matchinglower(host), normalized path prefix (normalizeDomainPath), and physicalserverId./and/api) are permitted and do not collide.serverId) across applications, compose services, and preview deployments.db.transactionto protect against concurrent duplicate creation.updateDomainByIdavoids false-positive collisions when updating existing domain settings (ports, certificates).apps/dokploy/server/api/routers/domain.ts:TRPCErrorindomainRouter.createcatch block so theCONFLICTstatus code propagates cleanly to API clients.apps/dokploy/__test__/domains/domain-conflict-validation.test.ts:Fix concurrent duplicate saves, failed project copies, and blocked settings-only edits before merging.
Summary
Adds same-server checks for matching hosts and normalized paths across applications, Compose services, and preview deployments. Creates now preserve
CONFLICTerrors.Intent recorded: taieddie explicitly described normalized-path matching, including trailing-slash normalization, as intended.
Reviews (1) · Last reviewed commit: "feat(domain): block assigning conflictin..." · Reviewed by Greptile