The Mounts feature (application/compose/database services) let any
member with volume:create access set an arbitrary bind mount hostPath,
with zero validation. Since generateBindMounts wires that path straight
into the Swarm service spec, a non-privileged member could mount
/var/run/docker.sock, /etc/dokploy, or other host-critical paths and
escalate to root on the Dokploy control host - same impact as bringing
your own docker-compose.yml with a docker.sock mount, but reachable
through the plain Mounts UI on any service type.
Add isDangerousBindMountPath() with a fixed blocklist of host-critical
paths (docker.sock, /etc/dokploy, /root, /boot, /proc, /sys, docker's
data dir) and isPrivilegedOrgRole(); mount.create/update now reject a
bind mount targeting one of those paths unless the caller is the org's
owner or admin. Single-user self-hosted installs are unaffected - the
sole user is always the owner.
findComposeById embedded the full github/gitlab/bitbucket/gitea
relations (client secrets, OAuth tokens, private keys, app passwords)
and compose.one only used canEditDeployGitSource to set a
hasGitProviderAccess flag, never to hide the fields — so any member
with read access to a compose service got the connected git
provider's raw credentials, regardless of their access to that
provider itself.
Exclude the same secret columns findApplicationById already excludes.
Deploys are unaffected: the actual clone step always re-fetches the
provider fresh by id (findGithubById/findGitlabById/...), it never
reads secrets off the embedded relation.
gitlab.one, github.one, gitea.one and bitbucket.one returned the full
DB record (OAuth access/refresh tokens, client secrets, private keys,
webhook secrets, app passwords) to any org member who merely had
access to *use* a shared provider (sharedWithOrganization: true),
not just its owner or an org owner/admin.
Add canViewGitProviderSecrets() and null out the secret fields in
each .one response when the caller isn't the provider owner or an
org owner/admin.
Show the services attached to a remote server directly in its delete
confirmation modal, with a link to each service and a per-service
delete action, instead of only showing a generic 'has active
services' blocker.
writeTraefikConfigRemote moved from an execAsyncRemote echo command to
writeFileRemote (SFTP) a while back; the test still mocked the old
execAsyncRemote path and asserted on an echo command that no longer runs.
Adds a modal to select and import multiple secrets from an assigned
vault provider at once, instead of typing each ${{vault.x.y}} reference
by hand. Existing keys are skipped by default and can be overridden per
row; also adds an "Access all" shortcut to the vault provider assignment
picker.
Embedding the full YAML (base64-encoded) into a single SSH exec command
silently fails once the payload passes the SSH transport's packet size
ceiling (~90-100KB), truncating the dynamic config with no error
surfaced anywhere. Switch to SFTP, which has no such limit.
removeJob() for type 'schedule' computed the BullMQ repeatable key
using only the cron pattern, without the timezone. scheduleJob()
always registers the repeatable job with tz set (job.timezone || UTC),
so BullMQ's repeat key (name:jobId:endDate:tz:pattern) never matched
on removal — removeRepeatable silently no-op'd and the job kept
firing forever, surviving disable, delete, and reboots.
Forward timezone through removeJob (queue.ts), the /update-backup
lookup (index.ts), and the disable/delete call sites in the
schedule router.
Fixes#5233. Updating an enabled schedule (cron/timezone change) called
schedule() (create-backup), which only adds a new repeatable job and
never removes the one registered under the old cron pattern, leaving
an orphaned BullMQ job running in Redis indefinitely. Switched to
updateJob() (update-backup), which looks up the currently registered
repeatable pattern, removes it, then re-adds — same pattern already
used in backup.ts for backups.
--project-directory was added unconditionally in 87b914996 to fix
relative bind mount resolution for git-based compose deploys with a
nested composePath (#5181). It also moves where build.context and the
generated .env resolve, breaking any compose file in a subdirectory
that has context: . alongside its Dockerfile (#5230), or interpolates
env vars (#5242).
Only pin --project-directory when the compose actually has mounts
configured; otherwise build.context and .env resolve against the
compose file's own directory like plain docker compose. Also pass
--env-file explicitly pointing at the generated .env next to the
compose file, so it's found even when --project-directory is pinned
(credit: tonnenpinguin, PR #5235).
Fixes#5230Fixes#5242
The loading branch added in the previous commit split the fallback cell
two ways: spinner while in flight, "No results." otherwise. That second
branch also catches the failure case -- when readStatsLogs errors,
statsLogs stays undefined and isLoading goes false, so a failed request
renders as a successful empty response.
The query's isError/error were not destructured. The cell now branches
three ways and reports the error through AlertBlock, matching how
ShowTraefikSystem surfaces a failed readDirectories query.
Trims and lowercases the CLI email arg to match the normalization
used elsewhere for user emails, so a differently-cased or
whitespace-padded email no longer falsely reports "User not found".
Previously reset-password only reset the owner account's password.
Passing an email as an argument now resets that specific user's
password instead, while omitting it keeps the existing owner-reset
behavior.
Also scopes the update to the credential (password-based) account
row via providerId, and fixes the success check to verify a row was
actually updated instead of always reporting success.