Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c04d56b
feat: export full keyring in backup encryption key file
Siumauricio Jul 10, 2026
12d3f18
fix(ui): typos
EvanSchleret Jul 12, 2026
f577778
fix: validate API key name length to prevent opaque 500
tanaymishra Jul 12, 2026
31380fd
refactor(auth): simplify SSO configuration by removing unnecessary va…
Siumauricio Jul 13, 2026
9142127
Merge pull request #4814 from Dokploy/feat/backup-encryption-key
Siumauricio Jul 13, 2026
7ba9818
Merge pull request #4806 from tanaymishra/fix/validate-api-key-name-l…
Siumauricio Jul 14, 2026
bc22d05
fix(compose): preserve named-volume access mode when adding suffix
Siumauricio Jul 14, 2026
4631ede
Merge pull request #4823 from Dokploy/fix/compose-volume-suffix-acces…
Siumauricio Jul 14, 2026
d5dd35c
fix(settings): allow clearing the server domain
Siumauricio Jul 14, 2026
de62aff
fix(ui): disambiguate repos with the same name in the repository sele…
Siumauricio Jul 14, 2026
c4596ff
refactor(ui): improve repository and branch display in popovers
Siumauricio Jul 14, 2026
9626c16
Merge pull request #4826 from Dokploy/fix/repo-selector-duplicate-names
Siumauricio Jul 14, 2026
bddc0c3
Merge pull request #4825 from Dokploy/fix/allow-clearing-server-domain
Siumauricio Jul 14, 2026
df3965a
Merge pull request #4800 from EvanSchleret/fix/typo
Siumauricio Jul 15, 2026
a71db21
feat: export full keyring in backup encryption key file
Siumauricio Jul 10, 2026
1cc0b14
fix(compose): preserve named-volume access mode when adding suffix
Siumauricio Jul 14, 2026
4dbb9fe
feat: selectively sync upstream canary fixes
windinternet Jul 15, 2026
1fecfcd
chore: record upstream canary sync baseline
windinternet Jul 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions apps/dokploy/__test__/api/api-key-name.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
import { API_KEY_NAME_MAX_LENGTH, apiKeyNameSchema } from "@/lib/api-keys";

describe("apiKeyNameSchema", () => {
it("rejects an empty name", () => {
expect(apiKeyNameSchema.safeParse("").success).toBe(false);
});

it("accepts the Better Auth maximum", () => {
expect(
apiKeyNameSchema.safeParse("a".repeat(API_KEY_NAME_MAX_LENGTH)).success,
).toBe(true);
});

it("rejects names longer than Better Auth accepts", () => {
expect(
apiKeyNameSchema.safeParse("a".repeat(API_KEY_NAME_MAX_LENGTH + 1))
.success,
).toBe(false);
});
});
33 changes: 33 additions & 0 deletions apps/dokploy/__test__/compose/volume/volume-services.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,39 @@ test("Add suffix to volumes declared directly in services", () => {
);
});

const composeFileAccessMode = `
version: "3.8"

services:
web:
image: nginx:alpine
volumes:
- web_config:/etc/nginx/conf.d:ro
- certs/sub:/etc/certs:Z
`;

test("Add suffix to volumes preserves access mode (:ro, :z, :Z)", () => {
const composeData = parse(composeFileAccessMode) as ComposeSpecification;

const suffix = generateRandomHash();

if (!composeData.services) {
return;
}

const updatedComposeData = addSuffixToVolumesInServices(
composeData.services,
suffix,
);

expect(updatedComposeData.web?.volumes).toContain(
`web_config-${suffix}:/etc/nginx/conf.d:ro`,
);
expect(updatedComposeData.web?.volumes).toContain(
`certs-${suffix}/sub:/etc/certs:Z`,
);
});

const composeFileTypeVolume = `
version: "3.8"

Expand Down
6 changes: 3 additions & 3 deletions apps/dokploy/__test__/env/encryption.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {
decryptValue,
encryptValue,
exportEncryptionKey,
exportEncryptionKeys,
isEncrypted,
} from "@dokploy/server/lib/encryption";
import { afterEach, describe, expect, it, vi } from "vitest";
Expand Down Expand Up @@ -44,8 +44,8 @@ describe("encryptValue / decryptValue", () => {
expect(() => decryptValue(tampered)).toThrow(/BETTER_AUTH_SECRET/);
});

it("exports the primary key as 32-byte hex for backups", () => {
expect(exportEncryptionKey()).toMatch(/^[0-9a-f]{64}$/);
it("exports the derived keys as 32-byte hex lines for backups", () => {
expect(exportEncryptionKeys()).toMatch(/^[0-9a-f]{64}(\n[0-9a-f]{64})*$/);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,9 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => {
"projects:application.general.providers.loadingShort",
)
: (repositories?.find(
(repo) => repo.name === field.value.repo,
(repo) =>
repo.name === field.value.repo &&
repo.owner.username === field.value.owner,
)?.name ??
t(
"projects:application.general.providers.fields.selectRepository",
Expand All @@ -342,7 +344,10 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => {
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<PopoverContent
className="w-[var(--radix-popover-trigger-width)] p-0"
align="start"
>
<Command>
<CommandInput
placeholder={t(
Expand Down Expand Up @@ -372,7 +377,7 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => {
<CommandGroup>
{repositories?.map((repo) => (
<CommandItem
value={repo.name}
value={`${repo.owner.username}/${repo.name}`}
key={repo.url}
onSelect={() => {
form.setValue("repository", {
Expand All @@ -383,16 +388,17 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => {
form.setValue("branch", "");
}}
>
<span className="flex items-center gap-2">
<span>{repo.name}</span>
<span className="flex min-w-0 items-center gap-2">
<span className="truncate">{repo.name}</span>
<span className="text-muted-foreground text-xs">
{repo.owner.username}
</span>
</span>
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
repo.name === field.value.repo
repo.name === field.value.repo &&
repo.owner.username === field.value.owner
? "opacity-100"
: "opacity-0",
)}
Expand Down Expand Up @@ -447,7 +453,10 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => {
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<PopoverContent
className="w-[var(--radix-popover-trigger-width)] p-0"
align="start"
>
<Command>
<CommandInput
placeholder={t(
Expand Down Expand Up @@ -485,7 +494,7 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => {
form.setValue("branch", branch.name);
}}
>
{branch.name}
<span className="truncate">{branch.name}</span>
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
)
: (repositories?.find(
(repo: GiteaRepository) =>
repo.name === field.value.repo,
repo.name === field.value.repo &&
repo.owner.username === field.value.owner,
)?.name ??
t(
"projects:application.general.providers.fields.selectRepository",
Expand All @@ -347,7 +348,10 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<PopoverContent
className="w-[var(--radix-popover-trigger-width)] p-0"
align="start"
>
<Command>
<CommandInput
placeholder={t(
Expand Down Expand Up @@ -385,7 +389,7 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
{repositories?.map((repo: GiteaRepository) => {
return (
<CommandItem
value={repo.name}
value={`${repo.owner.username}/${repo.name}`}
key={repo.url}
onSelect={() => {
form.setValue("repository", {
Expand All @@ -395,16 +399,20 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
form.setValue("branch", "");
}}
>
<span className="flex items-center gap-2">
<span>{repo.name}</span>
<span className="flex min-w-0 items-center gap-2">
<span className="truncate">
{repo.name}
</span>
<span className="text-muted-foreground text-xs">
{repo.owner.username}
</span>
</span>
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
repo.name === field.value.repo
repo.name === field.value.repo &&
repo.owner.username ===
field.value.owner
? "opacity-100"
: "opacity-0",
)}
Expand Down Expand Up @@ -461,7 +469,10 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<PopoverContent
className="w-[var(--radix-popover-trigger-width)] p-0"
align="start"
>
<Command>
<CommandInput
placeholder={t(
Expand Down Expand Up @@ -506,7 +517,7 @@ export const SaveGiteaProvider = ({ applicationId }: Props) => {
form.setValue("branch", branch.name);
}}
>
{branch.name}
<span className="truncate">{branch.name}</span>
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,14 +319,19 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
"projects:application.general.providers.loadingShort",
)
: (repositories?.find(
(repo) => repo.name === field.value.repo,
(repo) =>
repo.name === field.value.repo &&
repo.owner.login === field.value.owner,
)?.name ?? field.value.repo)}

<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<PopoverContent
className="w-[var(--radix-popover-trigger-width)] p-0"
align="start"
>
<Command>
<CommandInput
placeholder={t(
Expand Down Expand Up @@ -356,7 +361,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
<CommandGroup>
{repositories?.map((repo) => (
<CommandItem
value={repo.name}
value={`${repo.owner.login}/${repo.name}`}
key={repo.url}
onSelect={() => {
form.setValue("repository", {
Expand All @@ -366,16 +371,17 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
form.setValue("branch", "");
}}
>
<span className="flex items-center gap-2">
<span>{repo.name}</span>
<span className="flex min-w-0 items-center gap-2">
<span className="truncate">{repo.name}</span>
<span className="text-muted-foreground text-xs">
{repo.owner.login}
</span>
</span>
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
repo.name === field.value.repo
repo.name === field.value.repo &&
repo.owner.login === field.value.owner
? "opacity-100"
: "opacity-0",
)}
Expand Down Expand Up @@ -430,7 +436,10 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<PopoverContent
className="w-[var(--radix-popover-trigger-width)] p-0"
align="start"
>
<Command>
<CommandInput
placeholder={t(
Expand Down Expand Up @@ -468,7 +477,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
form.setValue("branch", branch.name);
}}
>
{branch.name}
<span className="truncate">{branch.name}</span>
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,10 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<PopoverContent
className="w-[var(--radix-popover-trigger-width)] p-0"
align="start"
>
<Command>
<CommandInput
placeholder={t(
Expand Down Expand Up @@ -392,8 +395,10 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
form.setValue("branch", "");
}}
>
<span className="flex items-center gap-2">
<span>{repo.name}</span>
<span className="flex min-w-0 items-center gap-2">
<span className="truncate">
{repo.name}
</span>
<span className="text-muted-foreground text-xs">
{repo.owner.username}
</span>
Expand Down Expand Up @@ -458,7 +463,10 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" align="start">
<PopoverContent
className="w-[var(--radix-popover-trigger-width)] p-0"
align="start"
>
<Command>
<CommandInput
placeholder={t(
Expand Down Expand Up @@ -496,7 +504,7 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => {
form.setValue("branch", branch.name);
}}
>
{branch.name}
<span className="truncate">{branch.name}</span>
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
Expand Down
Loading
Loading