Copy disabled (too large)
Download .txt
Showing preview only (16,118K chars total). Download the full file to get everything.
Repository: Dokploy/dokploy
Branch: canary
Commit: 6fb4a13a189b
Files: 1217
Total size: 23.8 MB
Directory structure:
gitextract_so1vxlep/
├── .devcontainer/
│ ├── Dockerfile
│ └── devcontainer.json
├── .dockerignore
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ └── feature-request.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── create-pr.yml
│ ├── deploy.yml
│ ├── dokploy.yml
│ ├── format.yml
│ ├── monitoring.yml
│ ├── pr-quality.yml
│ ├── pull-request.yml
│ └── sync-openapi-docs.yml
├── .gitignore
├── .nvmrc
├── .vscode/
│ ├── extensions.json
│ └── settings.json
├── CONTRIBUTING.md
├── Dockerfile
├── Dockerfile.cloud
├── Dockerfile.monitoring
├── Dockerfile.schedule
├── Dockerfile.server
├── GUIDES.md
├── LICENSE.MD
├── LICENSE_PROPRIETARY.md
├── README.md
├── SECURITY.md
├── TERMS_AND_CONDITIONS.md
├── apps/
│ ├── api/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── logger.ts
│ │ │ ├── schema.ts
│ │ │ ├── service.ts
│ │ │ └── utils.ts
│ │ └── tsconfig.json
│ ├── dokploy/
│ │ ├── .dockerignore
│ │ ├── .gitignore
│ │ ├── __test__/
│ │ │ ├── cluster/
│ │ │ │ └── upload.test.ts
│ │ │ ├── compose/
│ │ │ │ ├── compose.test.ts
│ │ │ │ ├── config/
│ │ │ │ │ ├── config-root.test.ts
│ │ │ │ │ ├── config-service.test.ts
│ │ │ │ │ └── config.test.ts
│ │ │ │ ├── domain/
│ │ │ │ │ ├── host-rule-format.test.ts
│ │ │ │ │ ├── labels.test.ts
│ │ │ │ │ ├── network-root.test.ts
│ │ │ │ │ └── network-service.test.ts
│ │ │ │ ├── network/
│ │ │ │ │ ├── network-root.test.ts
│ │ │ │ │ ├── network-service.test.ts
│ │ │ │ │ └── network.test.ts
│ │ │ │ ├── secrets/
│ │ │ │ │ ├── secret-root.test.ts
│ │ │ │ │ ├── secret-services.test.ts
│ │ │ │ │ └── secret.test.ts
│ │ │ │ ├── service/
│ │ │ │ │ ├── service-container-name.test.ts
│ │ │ │ │ ├── service-depends-on.test.ts
│ │ │ │ │ ├── service-extends.test.ts
│ │ │ │ │ ├── service-links.test.ts
│ │ │ │ │ ├── service-names.test.ts
│ │ │ │ │ ├── service.test.ts
│ │ │ │ │ └── sevice-volumes-from.test.ts
│ │ │ │ └── volume/
│ │ │ │ ├── volume-2.test.ts
│ │ │ │ ├── volume-root.test.ts
│ │ │ │ ├── volume-services.test.ts
│ │ │ │ └── volume.test.ts
│ │ │ ├── deploy/
│ │ │ │ ├── application.command.test.ts
│ │ │ │ ├── application.real.test.ts
│ │ │ │ ├── github.test.ts
│ │ │ │ └── soft-serve.test.ts
│ │ │ ├── drop/
│ │ │ │ ├── drop.test.ts
│ │ │ │ └── zips/
│ │ │ │ ├── folder1/
│ │ │ │ │ └── folder1.txt
│ │ │ │ ├── folder2/
│ │ │ │ │ └── folder2.txt
│ │ │ │ ├── folder3/
│ │ │ │ │ └── file3.txt
│ │ │ │ └── test.txt
│ │ │ ├── env/
│ │ │ │ ├── environment-access-fallback.test.ts
│ │ │ │ ├── environment.test.ts
│ │ │ │ ├── shared.test.ts
│ │ │ │ └── stack-environment.test.ts
│ │ │ ├── permissions/
│ │ │ │ ├── check-permission.test.ts
│ │ │ │ ├── enterprise-only-resources.test.ts
│ │ │ │ ├── resolve-permissions.test.ts
│ │ │ │ └── service-access.test.ts
│ │ │ ├── requests/
│ │ │ │ └── request.test.ts
│ │ │ ├── server/
│ │ │ │ └── mechanizeDockerContainer.test.ts
│ │ │ ├── setup.ts
│ │ │ ├── templates/
│ │ │ │ ├── config.template.test.ts
│ │ │ │ └── helpers.template.test.ts
│ │ │ ├── traefik/
│ │ │ │ ├── server/
│ │ │ │ │ └── update-server-config.test.ts
│ │ │ │ └── traefik.test.ts
│ │ │ ├── utils/
│ │ │ │ └── backups.test.ts
│ │ │ ├── vitest.config.ts
│ │ │ └── wss/
│ │ │ ├── readValidDirectory.test.ts
│ │ │ └── utils.test.ts
│ │ ├── components/
│ │ │ ├── dashboard/
│ │ │ │ ├── application/
│ │ │ │ │ ├── advanced/
│ │ │ │ │ │ ├── cluster/
│ │ │ │ │ │ │ ├── modify-swarm-settings.tsx
│ │ │ │ │ │ │ ├── show-cluster-settings.tsx
│ │ │ │ │ │ │ └── swarm-forms/
│ │ │ │ │ │ │ ├── endpoint-spec-form.tsx
│ │ │ │ │ │ │ ├── health-check-form.tsx
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── labels-form.tsx
│ │ │ │ │ │ │ ├── mode-form.tsx
│ │ │ │ │ │ │ ├── network-form.tsx
│ │ │ │ │ │ │ ├── placement-form.tsx
│ │ │ │ │ │ │ ├── restart-policy-form.tsx
│ │ │ │ │ │ │ ├── rollback-config-form.tsx
│ │ │ │ │ │ │ ├── stop-grace-period-form.tsx
│ │ │ │ │ │ │ ├── update-config-form.tsx
│ │ │ │ │ │ │ └── utils.ts
│ │ │ │ │ │ ├── general/
│ │ │ │ │ │ │ └── add-command.tsx
│ │ │ │ │ │ ├── import/
│ │ │ │ │ │ │ └── show-import.tsx
│ │ │ │ │ │ ├── ports/
│ │ │ │ │ │ │ ├── handle-ports.tsx
│ │ │ │ │ │ │ └── show-port.tsx
│ │ │ │ │ │ ├── redirects/
│ │ │ │ │ │ │ ├── handle-redirect.tsx
│ │ │ │ │ │ │ └── show-redirects.tsx
│ │ │ │ │ │ ├── security/
│ │ │ │ │ │ │ ├── handle-security.tsx
│ │ │ │ │ │ │ └── show-security.tsx
│ │ │ │ │ │ ├── show-build-server.tsx
│ │ │ │ │ │ ├── show-resources.tsx
│ │ │ │ │ │ ├── traefik/
│ │ │ │ │ │ │ ├── show-traefik-config.tsx
│ │ │ │ │ │ │ └── update-traefik-config.tsx
│ │ │ │ │ │ └── volumes/
│ │ │ │ │ │ ├── add-volumes.tsx
│ │ │ │ │ │ ├── show-volumes.tsx
│ │ │ │ │ │ └── update-volume.tsx
│ │ │ │ │ ├── build/
│ │ │ │ │ │ └── show.tsx
│ │ │ │ │ ├── deployments/
│ │ │ │ │ │ ├── cancel-queues.tsx
│ │ │ │ │ │ ├── clear-deployments.tsx
│ │ │ │ │ │ ├── kill-build.tsx
│ │ │ │ │ │ ├── refresh-token.tsx
│ │ │ │ │ │ ├── show-deployment.tsx
│ │ │ │ │ │ ├── show-deployments-modal.tsx
│ │ │ │ │ │ └── show-deployments.tsx
│ │ │ │ │ ├── domains/
│ │ │ │ │ │ ├── dns-helper-modal.tsx
│ │ │ │ │ │ ├── handle-domain.tsx
│ │ │ │ │ │ └── show-domains.tsx
│ │ │ │ │ ├── environment/
│ │ │ │ │ │ ├── show-enviroment.tsx
│ │ │ │ │ │ └── show.tsx
│ │ │ │ │ ├── general/
│ │ │ │ │ │ ├── generic/
│ │ │ │ │ │ │ ├── save-bitbucket-provider.tsx
│ │ │ │ │ │ │ ├── save-docker-provider.tsx
│ │ │ │ │ │ │ ├── save-drag-n-drop.tsx
│ │ │ │ │ │ │ ├── save-git-provider.tsx
│ │ │ │ │ │ │ ├── save-gitea-provider.tsx
│ │ │ │ │ │ │ ├── save-github-provider.tsx
│ │ │ │ │ │ │ ├── save-gitlab-provider.tsx
│ │ │ │ │ │ │ ├── show.tsx
│ │ │ │ │ │ │ └── unauthorized-git-provider.tsx
│ │ │ │ │ │ └── show.tsx
│ │ │ │ │ ├── logs/
│ │ │ │ │ │ └── show.tsx
│ │ │ │ │ ├── patches/
│ │ │ │ │ │ ├── create-file-dialog.tsx
│ │ │ │ │ │ ├── edit-patch-dialog.tsx
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── patch-editor.tsx
│ │ │ │ │ │ └── show-patches.tsx
│ │ │ │ │ ├── preview-deployments/
│ │ │ │ │ │ ├── add-preview-domain.tsx
│ │ │ │ │ │ ├── show-preview-deployments.tsx
│ │ │ │ │ │ └── show-preview-settings.tsx
│ │ │ │ │ ├── rollbacks/
│ │ │ │ │ │ ├── Backup
│ │ │ │ │ │ └── show-rollback-settings.tsx
│ │ │ │ │ ├── schedules/
│ │ │ │ │ │ ├── handle-schedules.tsx
│ │ │ │ │ │ ├── show-schedules.tsx
│ │ │ │ │ │ └── timezones.ts
│ │ │ │ │ ├── update-application.tsx
│ │ │ │ │ └── volume-backups/
│ │ │ │ │ ├── handle-volume-backups.tsx
│ │ │ │ │ ├── restore-volume-backups.tsx
│ │ │ │ │ └── show-volume-backups.tsx
│ │ │ │ ├── compose/
│ │ │ │ │ ├── advanced/
│ │ │ │ │ │ ├── add-command.tsx
│ │ │ │ │ │ └── add-isolation.tsx
│ │ │ │ │ ├── delete-service.tsx
│ │ │ │ │ ├── general/
│ │ │ │ │ │ ├── actions.tsx
│ │ │ │ │ │ ├── compose-file-editor.tsx
│ │ │ │ │ │ ├── generic/
│ │ │ │ │ │ │ ├── save-bitbucket-provider-compose.tsx
│ │ │ │ │ │ │ ├── save-git-provider-compose.tsx
│ │ │ │ │ │ │ ├── save-gitea-provider-compose.tsx
│ │ │ │ │ │ │ ├── save-github-provider-compose.tsx
│ │ │ │ │ │ │ ├── save-gitlab-provider-compose.tsx
│ │ │ │ │ │ │ └── show.tsx
│ │ │ │ │ │ ├── randomize-compose.tsx
│ │ │ │ │ │ ├── show-converted-compose.tsx
│ │ │ │ │ │ └── show.tsx
│ │ │ │ │ ├── logs/
│ │ │ │ │ │ ├── show-stack.tsx
│ │ │ │ │ │ └── show.tsx
│ │ │ │ │ └── update-compose.tsx
│ │ │ │ ├── database/
│ │ │ │ │ └── backups/
│ │ │ │ │ ├── handle-backup.tsx
│ │ │ │ │ ├── restore-backup.tsx
│ │ │ │ │ └── show-backups.tsx
│ │ │ │ ├── deployments/
│ │ │ │ │ ├── show-deployments-table.tsx
│ │ │ │ │ └── show-queue-table.tsx
│ │ │ │ ├── docker/
│ │ │ │ │ ├── config/
│ │ │ │ │ │ └── show-container-config.tsx
│ │ │ │ │ ├── logs/
│ │ │ │ │ │ ├── docker-logs-id.tsx
│ │ │ │ │ │ ├── line-count-filter.tsx
│ │ │ │ │ │ ├── show-docker-modal-logs.tsx
│ │ │ │ │ │ ├── show-docker-modal-stack-logs.tsx
│ │ │ │ │ │ ├── since-logs-filter.tsx
│ │ │ │ │ │ ├── status-logs-filter.tsx
│ │ │ │ │ │ ├── terminal-line.tsx
│ │ │ │ │ │ └── utils.ts
│ │ │ │ │ ├── show/
│ │ │ │ │ │ ├── colums.tsx
│ │ │ │ │ │ └── show-containers.tsx
│ │ │ │ │ └── terminal/
│ │ │ │ │ ├── docker-terminal-modal.tsx
│ │ │ │ │ └── docker-terminal.tsx
│ │ │ │ ├── file-system/
│ │ │ │ │ ├── show-traefik-file.tsx
│ │ │ │ │ └── show-traefik-system.tsx
│ │ │ │ ├── impersonation/
│ │ │ │ │ └── impersonation-bar.tsx
│ │ │ │ ├── mariadb/
│ │ │ │ │ ├── general/
│ │ │ │ │ │ ├── show-external-mariadb-credentials.tsx
│ │ │ │ │ │ ├── show-general-mariadb.tsx
│ │ │ │ │ │ └── show-internal-mariadb-credentials.tsx
│ │ │ │ │ └── update-mariadb.tsx
│ │ │ │ ├── mongo/
│ │ │ │ │ ├── general/
│ │ │ │ │ │ ├── show-external-mongo-credentials.tsx
│ │ │ │ │ │ ├── show-general-mongo.tsx
│ │ │ │ │ │ └── show-internal-mongo-credentials.tsx
│ │ │ │ │ └── update-mongo.tsx
│ │ │ │ ├── monitoring/
│ │ │ │ │ ├── free/
│ │ │ │ │ │ └── container/
│ │ │ │ │ │ ├── docker-block-chart.tsx
│ │ │ │ │ │ ├── docker-cpu-chart.tsx
│ │ │ │ │ │ ├── docker-disk-chart.tsx
│ │ │ │ │ │ ├── docker-memory-chart.tsx
│ │ │ │ │ │ ├── docker-network-chart.tsx
│ │ │ │ │ │ ├── show-free-compose-monitoring.tsx
│ │ │ │ │ │ └── show-free-container-monitoring.tsx
│ │ │ │ │ └── paid/
│ │ │ │ │ ├── container/
│ │ │ │ │ │ ├── container-block-chart.tsx
│ │ │ │ │ │ ├── container-cpu-chart.tsx
│ │ │ │ │ │ ├── container-memory-chart.tsx
│ │ │ │ │ │ ├── container-network-chart.tsx
│ │ │ │ │ │ ├── show-paid-compose-monitoring.tsx
│ │ │ │ │ │ └── show-paid-container-monitoring.tsx
│ │ │ │ │ └── servers/
│ │ │ │ │ ├── cpu-chart.tsx
│ │ │ │ │ ├── disk-chart.tsx
│ │ │ │ │ ├── memory-chart.tsx
│ │ │ │ │ ├── network-chart.tsx
│ │ │ │ │ └── show-paid-monitoring.tsx
│ │ │ │ ├── mysql/
│ │ │ │ │ ├── general/
│ │ │ │ │ │ ├── show-external-mysql-credentials.tsx
│ │ │ │ │ │ ├── show-general-mysql.tsx
│ │ │ │ │ │ └── show-internal-mysql-credentials.tsx
│ │ │ │ │ └── update-mysql.tsx
│ │ │ │ ├── organization/
│ │ │ │ │ └── handle-organization.tsx
│ │ │ │ ├── postgres/
│ │ │ │ │ ├── advanced/
│ │ │ │ │ │ └── show-custom-command.tsx
│ │ │ │ │ ├── general/
│ │ │ │ │ │ ├── show-external-postgres-credentials.tsx
│ │ │ │ │ │ ├── show-general-postgres.tsx
│ │ │ │ │ │ └── show-internal-postgres-credentials.tsx
│ │ │ │ │ └── update-postgres.tsx
│ │ │ │ ├── project/
│ │ │ │ │ ├── add-ai-assistant.tsx
│ │ │ │ │ ├── add-application.tsx
│ │ │ │ │ ├── add-compose.tsx
│ │ │ │ │ ├── add-database.tsx
│ │ │ │ │ ├── add-template.tsx
│ │ │ │ │ ├── advanced-environment-selector.tsx
│ │ │ │ │ ├── ai/
│ │ │ │ │ │ ├── step-one.tsx
│ │ │ │ │ │ ├── step-three.tsx
│ │ │ │ │ │ ├── step-two.tsx
│ │ │ │ │ │ └── template-generator.tsx
│ │ │ │ │ ├── duplicate-project.tsx
│ │ │ │ │ └── environment-variables.tsx
│ │ │ │ ├── projects/
│ │ │ │ │ ├── handle-project.tsx
│ │ │ │ │ ├── project-environment.tsx
│ │ │ │ │ └── show.tsx
│ │ │ │ ├── redis/
│ │ │ │ │ ├── general/
│ │ │ │ │ │ ├── show-external-redis-credentials.tsx
│ │ │ │ │ │ ├── show-general-redis.tsx
│ │ │ │ │ │ └── show-internal-redis-credentials.tsx
│ │ │ │ │ └── update-redis.tsx
│ │ │ │ ├── requests/
│ │ │ │ │ ├── columns.tsx
│ │ │ │ │ ├── request-distribution-chart.tsx
│ │ │ │ │ ├── requests-table.tsx
│ │ │ │ │ ├── show-requests.tsx
│ │ │ │ │ └── status-request-filter.tsx
│ │ │ │ ├── search-command.tsx
│ │ │ │ ├── settings/
│ │ │ │ │ ├── ai-form.tsx
│ │ │ │ │ ├── api/
│ │ │ │ │ │ ├── add-api-key.tsx
│ │ │ │ │ │ └── show-api-keys.tsx
│ │ │ │ │ ├── billing/
│ │ │ │ │ │ ├── show-billing-invoices.tsx
│ │ │ │ │ │ ├── show-billing.tsx
│ │ │ │ │ │ ├── show-invoices.tsx
│ │ │ │ │ │ └── show-welcome-dokploy.tsx
│ │ │ │ │ ├── certificates/
│ │ │ │ │ │ ├── add-certificate.tsx
│ │ │ │ │ │ ├── show-certificates.tsx
│ │ │ │ │ │ └── utils.ts
│ │ │ │ │ ├── cluster/
│ │ │ │ │ │ ├── nodes/
│ │ │ │ │ │ │ ├── add-node.tsx
│ │ │ │ │ │ │ ├── manager/
│ │ │ │ │ │ │ │ └── add-manager.tsx
│ │ │ │ │ │ │ ├── show-node-data.tsx
│ │ │ │ │ │ │ ├── show-nodes-modal.tsx
│ │ │ │ │ │ │ ├── show-nodes.tsx
│ │ │ │ │ │ │ └── workers/
│ │ │ │ │ │ │ └── add-worker.tsx
│ │ │ │ │ │ └── registry/
│ │ │ │ │ │ ├── handle-registry.tsx
│ │ │ │ │ │ └── show-registry.tsx
│ │ │ │ │ ├── destination/
│ │ │ │ │ │ ├── constants.ts
│ │ │ │ │ │ ├── handle-destinations.tsx
│ │ │ │ │ │ └── show-destinations.tsx
│ │ │ │ │ ├── git/
│ │ │ │ │ │ ├── bitbucket/
│ │ │ │ │ │ │ ├── add-bitbucket-provider.tsx
│ │ │ │ │ │ │ └── edit-bitbucket-provider.tsx
│ │ │ │ │ │ ├── gitea/
│ │ │ │ │ │ │ ├── add-gitea-provider.tsx
│ │ │ │ │ │ │ └── edit-gitea-provider.tsx
│ │ │ │ │ │ ├── github/
│ │ │ │ │ │ │ ├── add-github-provider.tsx
│ │ │ │ │ │ │ └── edit-github-provider.tsx
│ │ │ │ │ │ ├── gitlab/
│ │ │ │ │ │ │ ├── add-gitlab-provider.tsx
│ │ │ │ │ │ │ └── edit-gitlab-provider.tsx
│ │ │ │ │ │ └── show-git-providers.tsx
│ │ │ │ │ ├── handle-ai.tsx
│ │ │ │ │ ├── linking-account/
│ │ │ │ │ │ └── linking-account.tsx
│ │ │ │ │ ├── notifications/
│ │ │ │ │ │ ├── handle-notifications.tsx
│ │ │ │ │ │ └── show-notifications.tsx
│ │ │ │ │ ├── profile/
│ │ │ │ │ │ ├── configure-2fa.tsx
│ │ │ │ │ │ ├── enable-2fa.tsx
│ │ │ │ │ │ └── profile-form.tsx
│ │ │ │ │ ├── servers/
│ │ │ │ │ │ ├── actions/
│ │ │ │ │ │ │ ├── show-dokploy-actions.tsx
│ │ │ │ │ │ │ ├── show-server-actions.tsx
│ │ │ │ │ │ │ ├── show-storage-actions.tsx
│ │ │ │ │ │ │ ├── show-traefik-actions.tsx
│ │ │ │ │ │ │ └── toggle-docker-cleanup.tsx
│ │ │ │ │ │ ├── edit-script.tsx
│ │ │ │ │ │ ├── gpu-support-modal.tsx
│ │ │ │ │ │ ├── gpu-support.tsx
│ │ │ │ │ │ ├── handle-servers.tsx
│ │ │ │ │ │ ├── security-audit.tsx
│ │ │ │ │ │ ├── setup-monitoring.tsx
│ │ │ │ │ │ ├── setup-server.tsx
│ │ │ │ │ │ ├── show-docker-containers-modal.tsx
│ │ │ │ │ │ ├── show-monitoring-modal.tsx
│ │ │ │ │ │ ├── show-schedules-modal.tsx
│ │ │ │ │ │ ├── show-servers.tsx
│ │ │ │ │ │ ├── show-swarm-overview-modal.tsx
│ │ │ │ │ │ ├── show-traefik-file-system-modal.tsx
│ │ │ │ │ │ ├── validate-server.tsx
│ │ │ │ │ │ └── welcome-stripe/
│ │ │ │ │ │ ├── create-server.tsx
│ │ │ │ │ │ ├── create-ssh-key.tsx
│ │ │ │ │ │ ├── setup.tsx
│ │ │ │ │ │ ├── verify.tsx
│ │ │ │ │ │ └── welcome-suscription.tsx
│ │ │ │ │ ├── ssh-keys/
│ │ │ │ │ │ ├── handle-ssh-keys.tsx
│ │ │ │ │ │ └── show-ssh-keys.tsx
│ │ │ │ │ ├── tags/
│ │ │ │ │ │ ├── handle-tag.tsx
│ │ │ │ │ │ └── tag-manager.tsx
│ │ │ │ │ ├── users/
│ │ │ │ │ │ ├── add-invitation.tsx
│ │ │ │ │ │ ├── add-permissions.tsx
│ │ │ │ │ │ ├── change-role.tsx
│ │ │ │ │ │ ├── show-invitations.tsx
│ │ │ │ │ │ └── show-users.tsx
│ │ │ │ │ ├── web-domain.tsx
│ │ │ │ │ ├── web-server/
│ │ │ │ │ │ ├── docker-terminal-modal.tsx
│ │ │ │ │ │ ├── edit-traefik-env.tsx
│ │ │ │ │ │ ├── local-server-config.tsx
│ │ │ │ │ │ ├── manage-traefik-ports.tsx
│ │ │ │ │ │ ├── show-modal-logs.tsx
│ │ │ │ │ │ ├── terminal-modal.tsx
│ │ │ │ │ │ ├── terminal.tsx
│ │ │ │ │ │ ├── toggle-auto-check-updates.tsx
│ │ │ │ │ │ ├── update-server-ip.tsx
│ │ │ │ │ │ ├── update-server.tsx
│ │ │ │ │ │ └── update-webserver.tsx
│ │ │ │ │ └── web-server.tsx
│ │ │ │ ├── shared/
│ │ │ │ │ ├── rebuild-database.tsx
│ │ │ │ │ └── show-database-advanced-settings.tsx
│ │ │ │ └── swarm/
│ │ │ │ ├── applications/
│ │ │ │ │ ├── columns.tsx
│ │ │ │ │ ├── data-table.tsx
│ │ │ │ │ └── show-applications.tsx
│ │ │ │ ├── details/
│ │ │ │ │ ├── details-card.tsx
│ │ │ │ │ └── show-node-config.tsx
│ │ │ │ └── monitoring-card.tsx
│ │ │ ├── icons/
│ │ │ │ ├── data-tools-icons.tsx
│ │ │ │ └── notification-icons.tsx
│ │ │ ├── layouts/
│ │ │ │ ├── dashboard-layout.tsx
│ │ │ │ ├── onboarding-layout.tsx
│ │ │ │ ├── side.tsx
│ │ │ │ ├── update-server.tsx
│ │ │ │ └── user-nav.tsx
│ │ │ ├── proprietary/
│ │ │ │ ├── audit-logs/
│ │ │ │ │ ├── columns.tsx
│ │ │ │ │ ├── data-table.tsx
│ │ │ │ │ └── show-audit-logs.tsx
│ │ │ │ ├── auth/
│ │ │ │ │ ├── sign-in-with-github.tsx
│ │ │ │ │ └── sign-in-with-google.tsx
│ │ │ │ ├── enterprise-feature-gate.tsx
│ │ │ │ ├── license-keys/
│ │ │ │ │ └── license-key.tsx
│ │ │ │ ├── roles/
│ │ │ │ │ └── manage-custom-roles.tsx
│ │ │ │ ├── sso/
│ │ │ │ │ ├── register-oidc-dialog.tsx
│ │ │ │ │ ├── register-saml-dialog.tsx
│ │ │ │ │ ├── sign-in-with-sso.tsx
│ │ │ │ │ └── sso-settings.tsx
│ │ │ │ └── whitelabeling/
│ │ │ │ ├── whitelabeling-preview.tsx
│ │ │ │ ├── whitelabeling-provider.tsx
│ │ │ │ └── whitelabeling-settings.tsx
│ │ │ ├── shared/
│ │ │ │ ├── ChatwootWidget.tsx
│ │ │ │ ├── HubSpotWidget.tsx
│ │ │ │ ├── advance-breadcrumb.tsx
│ │ │ │ ├── alert-block.tsx
│ │ │ │ ├── breadcrumb-sidebar.tsx
│ │ │ │ ├── code-editor.tsx
│ │ │ │ ├── compose-spec.json
│ │ │ │ ├── date-tooltip.tsx
│ │ │ │ ├── dialog-action.tsx
│ │ │ │ ├── drawer-logs.tsx
│ │ │ │ ├── focus-shortcut-input.tsx
│ │ │ │ ├── logo.tsx
│ │ │ │ ├── status-tooltip.tsx
│ │ │ │ ├── tag-badge.tsx
│ │ │ │ ├── tag-filter.tsx
│ │ │ │ ├── tag-selector.tsx
│ │ │ │ └── toggle-visibility-input.tsx
│ │ │ └── ui/
│ │ │ ├── accordion.tsx
│ │ │ ├── alert-dialog.tsx
│ │ │ ├── alert.tsx
│ │ │ ├── avatar.tsx
│ │ │ ├── badge.tsx
│ │ │ ├── breadcrumb.tsx
│ │ │ ├── button.tsx
│ │ │ ├── calendar.tsx
│ │ │ ├── card.tsx
│ │ │ ├── chart.tsx
│ │ │ ├── checkbox.tsx
│ │ │ ├── collapsible.tsx
│ │ │ ├── command.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── dropdown-menu.tsx
│ │ │ ├── dropzone.tsx
│ │ │ ├── file-tree.tsx
│ │ │ ├── form.tsx
│ │ │ ├── input-otp.tsx
│ │ │ ├── input.tsx
│ │ │ ├── label.tsx
│ │ │ ├── modeToggle.tsx
│ │ │ ├── number-input.tsx
│ │ │ ├── popover.tsx
│ │ │ ├── progress.tsx
│ │ │ ├── radio-group.tsx
│ │ │ ├── scroll-area.tsx
│ │ │ ├── secrets.tsx
│ │ │ ├── select.tsx
│ │ │ ├── separator.tsx
│ │ │ ├── sheet.tsx
│ │ │ ├── sidebar.tsx
│ │ │ ├── skeleton.tsx
│ │ │ ├── sonner.tsx
│ │ │ ├── switch.tsx
│ │ │ ├── table.tsx
│ │ │ ├── tabs.tsx
│ │ │ ├── textarea.tsx
│ │ │ ├── time-badge.tsx
│ │ │ ├── toggle.tsx
│ │ │ └── tooltip.tsx
│ │ ├── components.json
│ │ ├── docker/
│ │ │ ├── build.sh
│ │ │ ├── feat.sh
│ │ │ └── push.sh
│ │ ├── drizzle/
│ │ │ ├── 0000_reflective_puck.sql
│ │ │ ├── 0001_striped_tattoo.sql
│ │ │ ├── 0002_ambiguous_carlie_cooper.sql
│ │ │ ├── 0003_square_lightspeed.sql
│ │ │ ├── 0004_nice_tenebrous.sql
│ │ │ ├── 0005_cute_terror.sql
│ │ │ ├── 0006_oval_jimmy_woo.sql
│ │ │ ├── 0007_cute_guardsmen.sql
│ │ │ ├── 0008_lazy_sage.sql
│ │ │ ├── 0009_majestic_spencer_smythe.sql
│ │ │ ├── 0010_lean_black_widow.sql
│ │ │ ├── 0011_petite_calypso.sql
│ │ │ ├── 0012_chubby_umar.sql
│ │ │ ├── 0013_blushing_starjammers.sql
│ │ │ ├── 0014_same_hammerhead.sql
│ │ │ ├── 0015_fearless_callisto.sql
│ │ │ ├── 0016_chunky_leopardon.sql
│ │ │ ├── 0017_minor_post.sql
│ │ │ ├── 0018_careful_killmonger.sql
│ │ │ ├── 0019_heavy_freak.sql
│ │ │ ├── 0020_fantastic_slapstick.sql
│ │ │ ├── 0021_premium_sebastian_shaw.sql
│ │ │ ├── 0022_warm_colonel_america.sql
│ │ │ ├── 0023_icy_maverick.sql
│ │ │ ├── 0024_dapper_supernaut.sql
│ │ │ ├── 0025_lying_mephisto.sql
│ │ │ ├── 0026_known_dormammu.sql
│ │ │ ├── 0027_red_lady_bullseye.sql
│ │ │ ├── 0028_jittery_eternity.sql
│ │ │ ├── 0029_colossal_zodiak.sql
│ │ │ ├── 0030_little_kabuki.sql
│ │ │ ├── 0031_steep_vulture.sql
│ │ │ ├── 0032_flashy_shadow_king.sql
│ │ │ ├── 0033_white_hawkeye.sql
│ │ │ ├── 0034_aspiring_secret_warriors.sql
│ │ │ ├── 0035_cool_gravity.sql
│ │ │ ├── 0036_tired_ronan.sql
│ │ │ ├── 0037_legal_namor.sql
│ │ │ ├── 0038_rapid_landau.sql
│ │ │ ├── 0039_many_tiger_shark.sql
│ │ │ ├── 0040_graceful_wolfsbane.sql
│ │ │ ├── 0041_huge_bruce_banner.sql
│ │ │ ├── 0042_fancy_havok.sql
│ │ │ ├── 0043_closed_naoko.sql
│ │ │ ├── 0044_sour_true_believers.sql
│ │ │ ├── 0045_smiling_blur.sql
│ │ │ ├── 0046_purple_sleeper.sql
│ │ │ ├── 0047_tidy_revanche.sql
│ │ │ ├── 0048_flat_expediter.sql
│ │ │ ├── 0049_dark_leopardon.sql
│ │ │ ├── 0050_nappy_wrecker.sql
│ │ │ ├── 0051_hard_gorgon.sql
│ │ │ ├── 0052_bumpy_luckman.sql
│ │ │ ├── 0053_broken_kulan_gath.sql
│ │ │ ├── 0054_nervous_spencer_smythe.sql
│ │ │ ├── 0055_next_serpent_society.sql
│ │ │ ├── 0056_majestic_skaar.sql
│ │ │ ├── 0057_tricky_living_tribunal.sql
│ │ │ ├── 0058_brown_sharon_carter.sql
│ │ │ ├── 0059_striped_bill_hollister.sql
│ │ │ ├── 0060_disable-aggressive-cache.sql
│ │ │ ├── 0061_many_molten_man.sql
│ │ │ ├── 0062_slippery_white_tiger.sql
│ │ │ ├── 0063_panoramic_dreadnoughts.sql
│ │ │ ├── 0064_previous_agent_brand.sql
│ │ │ ├── 0065_daily_zaladane.sql
│ │ │ ├── 0066_yielding_echo.sql
│ │ │ ├── 0067_condemned_sugar_man.sql
│ │ │ ├── 0068_complex_rhino.sql
│ │ │ ├── 0069_legal_bill_hollister.sql
│ │ │ ├── 0070_useful_serpent_society.sql
│ │ │ ├── 0071_flaky_black_queen.sql
│ │ │ ├── 0072_green_susan_delgado.sql
│ │ │ ├── 0073_hot_domino.sql
│ │ │ ├── 0074_black_quasar.sql
│ │ │ ├── 0075_young_typhoid_mary.sql
│ │ │ ├── 0076_young_sharon_ventura.sql
│ │ │ ├── 0077_chemical_dreadnoughts.sql
│ │ │ ├── 0078_uneven_omega_sentinel.sql
│ │ │ ├── 0079_bizarre_wendell_rand.sql
│ │ │ ├── 0080_sleepy_sinister_six.sql
│ │ │ ├── 0081_lovely_mentallo.sql
│ │ │ ├── 0082_clean_mandarin.sql
│ │ │ ├── 0083_parallel_stranger.sql
│ │ │ ├── 0084_thin_iron_lad.sql
│ │ │ ├── 0085_equal_captain_stacy.sql
│ │ │ ├── 0086_rainy_gertrude_yorkes.sql
│ │ │ ├── 0087_lively_risque.sql
│ │ │ ├── 0088_illegal_ma_gnuci.sql
│ │ │ ├── 0089_noisy_sandman.sql
│ │ │ ├── 0090_clean_wolf_cub.sql
│ │ │ ├── 0091_spotty_kulan_gath.sql
│ │ │ ├── 0092_stiff_the_watchers.sql
│ │ │ ├── 0093_nice_gorilla_man.sql
│ │ │ ├── 0094_numerous_carmella_unuscione.sql
│ │ │ ├── 0095_curly_justice.sql
│ │ │ ├── 0096_small_shaman.sql
│ │ │ ├── 0097_hard_lizard.sql
│ │ │ ├── 0098_conscious_chat.sql
│ │ │ ├── 0099_wise_golden_guardian.sql
│ │ │ ├── 0100_purple_rogue.sql
│ │ │ ├── 0101_moaning_blazing_skull.sql
│ │ │ ├── 0102_opposite_grandmaster.sql
│ │ │ ├── 0103_cultured_pestilence.sql
│ │ │ ├── 0104_omniscient_randall.sql
│ │ │ ├── 0105_clumsy_quicksilver.sql
│ │ │ ├── 0106_purple_maggott.sql
│ │ │ ├── 0107_loud_kang.sql
│ │ │ ├── 0108_lazy_next_avengers.sql
│ │ │ ├── 0109_remarkable_sauron.sql
│ │ │ ├── 0110_red_psynapse.sql
│ │ │ ├── 0111_mushy_wolfsbane.sql
│ │ │ ├── 0112_freezing_skrulls.sql
│ │ │ ├── 0113_complete_rafael_vega.sql
│ │ │ ├── 0114_dry_black_tom.sql
│ │ │ ├── 0115_serious_black_bird.sql
│ │ │ ├── 0116_amusing_firedrake.sql
│ │ │ ├── 0117_lumpy_nuke.sql
│ │ │ ├── 0118_loose_anita_blake.sql
│ │ │ ├── 0119_bouncy_morbius.sql
│ │ │ ├── 0120_lame_captain_midlands.sql
│ │ │ ├── 0121_rainy_cargill.sql
│ │ │ ├── 0122_absent_frightful_four.sql
│ │ │ ├── 0123_cloudy_piledriver.sql
│ │ │ ├── 0124_certain_cloak.sql
│ │ │ ├── 0125_neat_the_phantom.sql
│ │ │ ├── 0126_nifty_monster_badoon.sql
│ │ │ ├── 0127_superb_alice.sql
│ │ │ ├── 0128_hard_falcon.sql
│ │ │ ├── 0129_pale_roughhouse.sql
│ │ │ ├── 0130_perpetual_screwball.sql
│ │ │ ├── 0131_volatile_beast.sql
│ │ │ ├── 0132_clean_layla_miller.sql
│ │ │ ├── 0133_striped_the_order.sql
│ │ │ ├── 0134_strong_hercules.sql
│ │ │ ├── 0135_illegal_magik.sql
│ │ │ ├── 0136_tidy_puff_adder.sql
│ │ │ ├── 0137_colossal_sally_floyd.sql
│ │ │ ├── 0138_pretty_ironclad.sql
│ │ │ ├── 0139_brave_bloodstorm.sql
│ │ │ ├── 0140_lame_mattie_franklin.sql
│ │ │ ├── 0141_plain_earthquake.sql
│ │ │ ├── 0142_outstanding_tusk.sql
│ │ │ ├── 0143_brown_ultron.sql
│ │ │ ├── 0144_odd_gunslinger.sql
│ │ │ ├── 0145_remarkable_titania.sql
│ │ │ ├── 0146_bumpy_morg.sql
│ │ │ ├── 0147_right_lake.sql
│ │ │ ├── 0148_futuristic_bullseye.sql
│ │ │ ├── 0149_rare_radioactive_man.sql
│ │ │ ├── 0150_nappy_blue_blade.sql
│ │ │ ├── 0151_modern_sunfire.sql
│ │ │ ├── 0152_odd_firelord.sql
│ │ │ └── meta/
│ │ │ ├── 0000_snapshot.json
│ │ │ ├── 0001_snapshot.json
│ │ │ ├── 0002_snapshot.json
│ │ │ ├── 0003_snapshot.json
│ │ │ ├── 0004_snapshot.json
│ │ │ ├── 0005_snapshot.json
│ │ │ ├── 0006_snapshot.json
│ │ │ ├── 0007_snapshot.json
│ │ │ ├── 0008_snapshot.json
│ │ │ ├── 0009_snapshot.json
│ │ │ ├── 0010_snapshot.json
│ │ │ ├── 0011_snapshot.json
│ │ │ ├── 0012_snapshot.json
│ │ │ ├── 0013_snapshot.json
│ │ │ ├── 0014_snapshot.json
│ │ │ ├── 0015_snapshot.json
│ │ │ ├── 0016_snapshot.json
│ │ │ ├── 0017_snapshot.json
│ │ │ ├── 0018_snapshot.json
│ │ │ ├── 0019_snapshot.json
│ │ │ ├── 0020_snapshot.json
│ │ │ ├── 0021_snapshot.json
│ │ │ ├── 0022_snapshot.json
│ │ │ ├── 0023_snapshot.json
│ │ │ ├── 0024_snapshot.json
│ │ │ ├── 0025_snapshot.json
│ │ │ ├── 0026_snapshot.json
│ │ │ ├── 0027_snapshot.json
│ │ │ ├── 0028_snapshot.json
│ │ │ ├── 0029_snapshot.json
│ │ │ ├── 0030_snapshot.json
│ │ │ ├── 0031_snapshot.json
│ │ │ ├── 0032_snapshot.json
│ │ │ ├── 0033_snapshot.json
│ │ │ ├── 0034_snapshot.json
│ │ │ ├── 0035_snapshot.json
│ │ │ ├── 0036_snapshot.json
│ │ │ ├── 0037_snapshot.json
│ │ │ ├── 0038_snapshot.json
│ │ │ ├── 0039_snapshot.json
│ │ │ ├── 0040_snapshot.json
│ │ │ ├── 0041_snapshot.json
│ │ │ ├── 0042_snapshot.json
│ │ │ ├── 0043_snapshot.json
│ │ │ ├── 0044_snapshot.json
│ │ │ ├── 0045_snapshot.json
│ │ │ ├── 0046_snapshot.json
│ │ │ ├── 0047_snapshot.json
│ │ │ ├── 0048_snapshot.json
│ │ │ ├── 0049_snapshot.json
│ │ │ ├── 0050_snapshot.json
│ │ │ ├── 0051_snapshot.json
│ │ │ ├── 0052_snapshot.json
│ │ │ ├── 0053_snapshot.json
│ │ │ ├── 0054_snapshot.json
│ │ │ ├── 0055_snapshot.json
│ │ │ ├── 0056_snapshot.json
│ │ │ ├── 0057_snapshot.json
│ │ │ ├── 0058_snapshot.json
│ │ │ ├── 0059_snapshot.json
│ │ │ ├── 0060_snapshot.json
│ │ │ ├── 0061_snapshot.json
│ │ │ ├── 0062_snapshot.json
│ │ │ ├── 0063_snapshot.json
│ │ │ ├── 0064_snapshot.json
│ │ │ ├── 0065_snapshot.json
│ │ │ ├── 0066_snapshot.json
│ │ │ ├── 0067_snapshot.json
│ │ │ ├── 0068_snapshot.json
│ │ │ ├── 0069_snapshot.json
│ │ │ ├── 0070_snapshot.json
│ │ │ ├── 0071_snapshot.json
│ │ │ ├── 0072_snapshot.json
│ │ │ ├── 0073_snapshot.json
│ │ │ ├── 0074_snapshot.json
│ │ │ ├── 0075_snapshot.json
│ │ │ ├── 0076_snapshot.json
│ │ │ ├── 0077_snapshot.json
│ │ │ ├── 0078_snapshot.json
│ │ │ ├── 0079_snapshot.json
│ │ │ ├── 0080_snapshot.json
│ │ │ ├── 0081_snapshot.json
│ │ │ ├── 0082_snapshot.json
│ │ │ ├── 0083_snapshot.json
│ │ │ ├── 0084_snapshot.json
│ │ │ ├── 0085_snapshot.json
│ │ │ ├── 0086_snapshot.json
│ │ │ ├── 0087_snapshot.json
│ │ │ ├── 0088_snapshot.json
│ │ │ ├── 0089_snapshot.json
│ │ │ ├── 0090_snapshot.json
│ │ │ ├── 0091_snapshot.json
│ │ │ ├── 0092_snapshot.json
│ │ │ ├── 0093_snapshot.json
│ │ │ ├── 0094_snapshot.json
│ │ │ ├── 0095_snapshot.json
│ │ │ ├── 0096_snapshot.json
│ │ │ ├── 0097_snapshot.json
│ │ │ ├── 0098_snapshot.json
│ │ │ ├── 0099_snapshot.json
│ │ │ ├── 0100_snapshot.json
│ │ │ ├── 0101_snapshot.json
│ │ │ ├── 0102_snapshot.json
│ │ │ ├── 0103_snapshot.json
│ │ │ ├── 0104_snapshot.json
│ │ │ ├── 0105_snapshot.json
│ │ │ ├── 0106_snapshot.json
│ │ │ ├── 0107_snapshot.json
│ │ │ ├── 0108_snapshot.json
│ │ │ ├── 0109_snapshot.json
│ │ │ ├── 0110_snapshot.json
│ │ │ ├── 0111_snapshot.json
│ │ │ ├── 0112_snapshot.json
│ │ │ ├── 0113_snapshot.json
│ │ │ ├── 0114_snapshot.json
│ │ │ ├── 0115_snapshot.json
│ │ │ ├── 0116_snapshot.json
│ │ │ ├── 0117_snapshot.json
│ │ │ ├── 0118_snapshot.json
│ │ │ ├── 0119_snapshot.json
│ │ │ ├── 0120_snapshot.json
│ │ │ ├── 0121_snapshot.json
│ │ │ ├── 0122_snapshot.json
│ │ │ ├── 0123_snapshot.json
│ │ │ ├── 0124_snapshot.json
│ │ │ ├── 0125_snapshot.json
│ │ │ ├── 0126_snapshot.json
│ │ │ ├── 0127_snapshot.json
│ │ │ ├── 0128_snapshot.json
│ │ │ ├── 0129_snapshot.json
│ │ │ ├── 0130_snapshot.json
│ │ │ ├── 0131_snapshot.json
│ │ │ ├── 0132_snapshot.json
│ │ │ ├── 0133_snapshot.json
│ │ │ ├── 0134_snapshot.json
│ │ │ ├── 0135_snapshot.json
│ │ │ ├── 0136_snapshot.json
│ │ │ ├── 0137_snapshot.json
│ │ │ ├── 0138_snapshot.json
│ │ │ ├── 0139_snapshot.json
│ │ │ ├── 0140_snapshot.json
│ │ │ ├── 0141_snapshot.json
│ │ │ ├── 0142_snapshot.json
│ │ │ ├── 0143_snapshot.json
│ │ │ ├── 0144_snapshot.json
│ │ │ ├── 0145_snapshot.json
│ │ │ ├── 0146_snapshot.json
│ │ │ ├── 0147_snapshot.json
│ │ │ ├── 0148_snapshot.json
│ │ │ ├── 0149_snapshot.json
│ │ │ ├── 0150_snapshot.json
│ │ │ ├── 0151_snapshot.json
│ │ │ ├── 0152_snapshot.json
│ │ │ ├── _journal.json
│ │ │ └── _journal.json.backup
│ │ ├── esbuild.config.ts
│ │ ├── hooks/
│ │ │ ├── use-health-check-after-mutation.ts
│ │ │ ├── use-keyboard-nav.tsx
│ │ │ ├── use-mobile.tsx
│ │ │ └── useLocalStorage.tsx
│ │ ├── lib/
│ │ │ ├── auth-client.ts
│ │ │ ├── avatar-utils.ts
│ │ │ ├── password-utils.ts
│ │ │ ├── slug.ts
│ │ │ └── utils.ts
│ │ ├── migration.ts
│ │ ├── next.config.mjs
│ │ ├── package.json
│ │ ├── pages/
│ │ │ ├── _app.tsx
│ │ │ ├── _document.tsx
│ │ │ ├── _error.tsx
│ │ │ ├── accept-invitation/
│ │ │ │ └── [accept-invitation].tsx
│ │ │ ├── api/
│ │ │ │ ├── [...trpc].ts
│ │ │ │ ├── auth/
│ │ │ │ │ └── [...all].ts
│ │ │ │ ├── deploy/
│ │ │ │ │ ├── [refreshToken].ts
│ │ │ │ │ ├── compose/
│ │ │ │ │ │ └── [refreshToken].ts
│ │ │ │ │ └── github.ts
│ │ │ │ ├── health.ts
│ │ │ │ ├── providers/
│ │ │ │ │ ├── gitea/
│ │ │ │ │ │ ├── authorize.ts
│ │ │ │ │ │ ├── callback.ts
│ │ │ │ │ │ └── helper.ts
│ │ │ │ │ ├── github/
│ │ │ │ │ │ ├── setup.ts
│ │ │ │ │ │ └── webhook.ts
│ │ │ │ │ └── gitlab/
│ │ │ │ │ └── callback.ts
│ │ │ │ ├── stripe/
│ │ │ │ │ └── webhook.ts
│ │ │ │ └── trpc/
│ │ │ │ └── [trpc].ts
│ │ │ ├── dashboard/
│ │ │ │ ├── deployments.tsx
│ │ │ │ ├── docker.tsx
│ │ │ │ ├── monitoring.tsx
│ │ │ │ ├── project/
│ │ │ │ │ └── [projectId]/
│ │ │ │ │ └── environment/
│ │ │ │ │ ├── [environmentId]/
│ │ │ │ │ │ └── services/
│ │ │ │ │ │ ├── application/
│ │ │ │ │ │ │ └── [applicationId].tsx
│ │ │ │ │ │ ├── compose/
│ │ │ │ │ │ │ └── [composeId].tsx
│ │ │ │ │ │ ├── mariadb/
│ │ │ │ │ │ │ └── [mariadbId].tsx
│ │ │ │ │ │ ├── mongo/
│ │ │ │ │ │ │ └── [mongoId].tsx
│ │ │ │ │ │ ├── mysql/
│ │ │ │ │ │ │ └── [mysqlId].tsx
│ │ │ │ │ │ ├── postgres/
│ │ │ │ │ │ │ └── [postgresId].tsx
│ │ │ │ │ │ └── redis/
│ │ │ │ │ │ └── [redisId].tsx
│ │ │ │ │ └── [environmentId].tsx
│ │ │ │ ├── projects.tsx
│ │ │ │ ├── requests.tsx
│ │ │ │ ├── schedules.tsx
│ │ │ │ ├── settings/
│ │ │ │ │ ├── ai.tsx
│ │ │ │ │ ├── audit-logs.tsx
│ │ │ │ │ ├── billing.tsx
│ │ │ │ │ ├── certificates.tsx
│ │ │ │ │ ├── cluster.tsx
│ │ │ │ │ ├── destinations.tsx
│ │ │ │ │ ├── git-providers.tsx
│ │ │ │ │ ├── invoices.tsx
│ │ │ │ │ ├── license.tsx
│ │ │ │ │ ├── notifications.tsx
│ │ │ │ │ ├── profile.tsx
│ │ │ │ │ ├── registry.tsx
│ │ │ │ │ ├── server.tsx
│ │ │ │ │ ├── servers.tsx
│ │ │ │ │ ├── ssh-keys.tsx
│ │ │ │ │ ├── sso.tsx
│ │ │ │ │ ├── tags.tsx
│ │ │ │ │ ├── users.tsx
│ │ │ │ │ └── whitelabeling.tsx
│ │ │ │ ├── swarm.tsx
│ │ │ │ └── traefik.tsx
│ │ │ ├── index.tsx
│ │ │ ├── invitation.tsx
│ │ │ ├── register.tsx
│ │ │ ├── reset-password.tsx
│ │ │ ├── send-reset-password.tsx
│ │ │ └── swagger.tsx
│ │ ├── postcss.config.cjs
│ │ ├── public/
│ │ │ ├── locales/
│ │ │ │ ├── az/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── de/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── en/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── es/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── fa/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── fr/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── id/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── it/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── ja/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── ko/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── kz/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── ml/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── nl/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── no/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── pl/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── pt-br/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── ru/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── tr/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── uk/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ ├── zh-Hans/
│ │ │ │ │ ├── common.json
│ │ │ │ │ └── settings.json
│ │ │ │ └── zh-Hant/
│ │ │ │ ├── common.json
│ │ │ │ └── settings.json
│ │ │ └── robots.txt
│ │ ├── reset-2fa.ts
│ │ ├── reset-password.ts
│ │ ├── scripts/
│ │ │ └── generate-openapi.ts
│ │ ├── server/
│ │ │ ├── api/
│ │ │ │ ├── root.ts
│ │ │ │ ├── routers/
│ │ │ │ │ ├── admin.ts
│ │ │ │ │ ├── ai.ts
│ │ │ │ │ ├── application.ts
│ │ │ │ │ ├── backup.ts
│ │ │ │ │ ├── bitbucket.ts
│ │ │ │ │ ├── certificate.ts
│ │ │ │ │ ├── cluster.ts
│ │ │ │ │ ├── compose.ts
│ │ │ │ │ ├── deployment.ts
│ │ │ │ │ ├── destination.ts
│ │ │ │ │ ├── docker.ts
│ │ │ │ │ ├── domain.ts
│ │ │ │ │ ├── environment.ts
│ │ │ │ │ ├── git-provider.ts
│ │ │ │ │ ├── gitea.ts
│ │ │ │ │ ├── github.ts
│ │ │ │ │ ├── gitlab.ts
│ │ │ │ │ ├── mariadb.ts
│ │ │ │ │ ├── mongo.ts
│ │ │ │ │ ├── mount.ts
│ │ │ │ │ ├── mysql.ts
│ │ │ │ │ ├── notification.ts
│ │ │ │ │ ├── organization.ts
│ │ │ │ │ ├── patch.ts
│ │ │ │ │ ├── port.ts
│ │ │ │ │ ├── postgres.ts
│ │ │ │ │ ├── preview-deployment.ts
│ │ │ │ │ ├── project.ts
│ │ │ │ │ ├── proprietary/
│ │ │ │ │ │ ├── audit-log.ts
│ │ │ │ │ │ ├── custom-role.ts
│ │ │ │ │ │ ├── license-key.ts
│ │ │ │ │ │ ├── sso.ts
│ │ │ │ │ │ └── whitelabeling.ts
│ │ │ │ │ ├── redirects.ts
│ │ │ │ │ ├── redis.ts
│ │ │ │ │ ├── registry.ts
│ │ │ │ │ ├── rollbacks.ts
│ │ │ │ │ ├── schedule.ts
│ │ │ │ │ ├── security.ts
│ │ │ │ │ ├── server.ts
│ │ │ │ │ ├── settings.ts
│ │ │ │ │ ├── ssh-key.ts
│ │ │ │ │ ├── stripe.ts
│ │ │ │ │ ├── swarm.ts
│ │ │ │ │ ├── tag.ts
│ │ │ │ │ ├── user.ts
│ │ │ │ │ └── volume-backups.ts
│ │ │ │ ├── trpc.ts
│ │ │ │ └── utils/
│ │ │ │ └── audit.ts
│ │ │ ├── db/
│ │ │ │ ├── drizzle.config.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── migration.ts
│ │ │ │ ├── reset.ts
│ │ │ │ ├── schema/
│ │ │ │ │ └── index.ts
│ │ │ │ └── validations/
│ │ │ │ ├── domain.ts
│ │ │ │ └── index.ts
│ │ │ ├── queues/
│ │ │ │ ├── deployments-queue.ts
│ │ │ │ ├── queue-types.ts
│ │ │ │ ├── queueSetup.ts
│ │ │ │ └── redis-connection.ts
│ │ │ ├── server.ts
│ │ │ ├── utils/
│ │ │ │ ├── backup.ts
│ │ │ │ ├── deploy.ts
│ │ │ │ ├── docker.ts
│ │ │ │ ├── enterprise.ts
│ │ │ │ └── stripe.ts
│ │ │ └── wss/
│ │ │ ├── docker-container-logs.ts
│ │ │ ├── docker-container-terminal.ts
│ │ │ ├── docker-stats.ts
│ │ │ ├── drawer-logs.ts
│ │ │ ├── listen-deployment.ts
│ │ │ ├── terminal.ts
│ │ │ └── utils.ts
│ │ ├── setup.ts
│ │ ├── styles/
│ │ │ └── globals.css
│ │ ├── tailwind.config.ts
│ │ ├── templates/
│ │ │ ├── templates.ts
│ │ │ └── utils/
│ │ │ └── index.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.server.json
│ │ ├── types/
│ │ │ └── chatwoot.d.ts
│ │ ├── utils/
│ │ │ ├── api.ts
│ │ │ ├── gitea-utils.ts
│ │ │ ├── hooks/
│ │ │ │ ├── use-debounce.ts
│ │ │ │ ├── use-url.ts
│ │ │ │ └── use-whitelabeling.ts
│ │ │ └── schema.ts
│ │ └── wait-for-postgres.ts
│ ├── monitoring/
│ │ ├── .gitignore
│ │ ├── LICENSE.md
│ │ ├── README.md
│ │ ├── config/
│ │ │ └── metrics.go
│ │ ├── containers/
│ │ │ ├── config.go
│ │ │ ├── monitor.go
│ │ │ └── types.go
│ │ ├── database/
│ │ │ ├── cleanup.go
│ │ │ ├── containers.go
│ │ │ ├── db.go
│ │ │ └── server.go
│ │ ├── go.mod
│ │ ├── go.sum
│ │ ├── main.go
│ │ ├── middleware/
│ │ │ └── auth.go
│ │ └── monitoring/
│ │ └── monitor.go
│ └── schedules/
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── src/
│ │ ├── index.ts
│ │ ├── logger.ts
│ │ ├── queue.ts
│ │ ├── schema.ts
│ │ ├── utils.ts
│ │ └── workers.ts
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── biome.json
├── package.json
├── packages/
│ └── server/
│ ├── auth-schema.ts
│ ├── auth-schema2.ts
│ ├── esbuild.config.ts
│ ├── package.json
│ ├── scripts/
│ │ ├── switchToDist.js
│ │ └── switchToSrc.js
│ ├── src/
│ │ ├── auth/
│ │ │ └── random-password.ts
│ │ ├── constants/
│ │ │ └── index.ts
│ │ ├── db/
│ │ │ ├── constants.ts
│ │ │ ├── index.ts
│ │ │ ├── schema/
│ │ │ │ ├── account.ts
│ │ │ │ ├── ai.ts
│ │ │ │ ├── application.ts
│ │ │ │ ├── audit-log.ts
│ │ │ │ ├── backups.ts
│ │ │ │ ├── bitbucket.ts
│ │ │ │ ├── certificate.ts
│ │ │ │ ├── compose.ts
│ │ │ │ ├── dbml.ts
│ │ │ │ ├── deployment.ts
│ │ │ │ ├── destination.ts
│ │ │ │ ├── domain.ts
│ │ │ │ ├── environment.ts
│ │ │ │ ├── git-provider.ts
│ │ │ │ ├── gitea.ts
│ │ │ │ ├── github.ts
│ │ │ │ ├── gitlab.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── mariadb.ts
│ │ │ │ ├── mongo.ts
│ │ │ │ ├── mount.ts
│ │ │ │ ├── mysql.ts
│ │ │ │ ├── notification.ts
│ │ │ │ ├── patch.ts
│ │ │ │ ├── port.ts
│ │ │ │ ├── postgres.ts
│ │ │ │ ├── preview-deployments.ts
│ │ │ │ ├── project.ts
│ │ │ │ ├── redirects.ts
│ │ │ │ ├── redis.ts
│ │ │ │ ├── registry.ts
│ │ │ │ ├── rollbacks.ts
│ │ │ │ ├── schedule.ts
│ │ │ │ ├── schema.dbml
│ │ │ │ ├── security.ts
│ │ │ │ ├── server.ts
│ │ │ │ ├── session.ts
│ │ │ │ ├── shared.ts
│ │ │ │ ├── ssh-key.ts
│ │ │ │ ├── sso.ts
│ │ │ │ ├── tag.ts
│ │ │ │ ├── user.ts
│ │ │ │ ├── utils.ts
│ │ │ │ ├── volume-backups.ts
│ │ │ │ └── web-server-settings.ts
│ │ │ └── validations/
│ │ │ ├── domain.ts
│ │ │ └── index.ts
│ │ ├── emails/
│ │ │ ├── .gitignore
│ │ │ ├── emails/
│ │ │ │ ├── build-failed.tsx
│ │ │ │ ├── build-success.tsx
│ │ │ │ ├── database-backup.tsx
│ │ │ │ ├── docker-cleanup.tsx
│ │ │ │ ├── dokploy-restart.tsx
│ │ │ │ ├── invitation.tsx
│ │ │ │ ├── notion-magic-link.tsx
│ │ │ │ ├── plaid-verify-identity.tsx
│ │ │ │ ├── stripe-welcome.tsx
│ │ │ │ ├── vercel-invite-user.tsx
│ │ │ │ └── volume-backup.tsx
│ │ │ ├── package.json
│ │ │ └── readme.md
│ │ ├── index.ts
│ │ ├── lib/
│ │ │ ├── access-control.ts
│ │ │ ├── auth.ts
│ │ │ └── logger.ts
│ │ ├── monitoring/
│ │ │ └── utils.ts
│ │ ├── services/
│ │ │ ├── admin.ts
│ │ │ ├── ai.ts
│ │ │ ├── application.ts
│ │ │ ├── backup.ts
│ │ │ ├── bitbucket.ts
│ │ │ ├── cdn.ts
│ │ │ ├── certificate.ts
│ │ │ ├── cluster.ts
│ │ │ ├── compose.ts
│ │ │ ├── deployment.ts
│ │ │ ├── destination.ts
│ │ │ ├── docker.ts
│ │ │ ├── domain.ts
│ │ │ ├── environment.ts
│ │ │ ├── git-provider.ts
│ │ │ ├── gitea.ts
│ │ │ ├── github.ts
│ │ │ ├── gitlab.ts
│ │ │ ├── mariadb.ts
│ │ │ ├── mongo.ts
│ │ │ ├── mount.ts
│ │ │ ├── mysql.ts
│ │ │ ├── notification.ts
│ │ │ ├── patch-repo.ts
│ │ │ ├── patch.ts
│ │ │ ├── permission.ts
│ │ │ ├── port.ts
│ │ │ ├── postgres.ts
│ │ │ ├── preview-deployment.ts
│ │ │ ├── project.ts
│ │ │ ├── proprietary/
│ │ │ │ ├── audit-log.ts
│ │ │ │ ├── license-key.ts
│ │ │ │ └── sso.ts
│ │ │ ├── redirect.ts
│ │ │ ├── redis.ts
│ │ │ ├── registry.ts
│ │ │ ├── rollbacks.ts
│ │ │ ├── schedule.ts
│ │ │ ├── security.ts
│ │ │ ├── server.ts
│ │ │ ├── settings.ts
│ │ │ ├── ssh-key.ts
│ │ │ ├── user.ts
│ │ │ ├── volume-backups.ts
│ │ │ └── web-server-settings.ts
│ │ ├── setup/
│ │ │ ├── config-paths.ts
│ │ │ ├── monitoring-setup.ts
│ │ │ ├── postgres-setup.ts
│ │ │ ├── redis-setup.ts
│ │ │ ├── server-audit.ts
│ │ │ ├── server-setup.ts
│ │ │ ├── server-validate.ts
│ │ │ ├── setup.ts
│ │ │ └── traefik-setup.ts
│ │ ├── templates/
│ │ │ ├── github.ts
│ │ │ ├── index.ts
│ │ │ └── processors.ts
│ │ ├── types/
│ │ │ ├── template.ts
│ │ │ └── with.ts
│ │ ├── utils/
│ │ │ ├── access-log/
│ │ │ │ ├── handler.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── ai/
│ │ │ │ ├── index.ts
│ │ │ │ └── select-ai-provider.ts
│ │ │ ├── backups/
│ │ │ │ ├── compose.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── mariadb.ts
│ │ │ │ ├── mongo.ts
│ │ │ │ ├── mysql.ts
│ │ │ │ ├── postgres.ts
│ │ │ │ ├── utils.ts
│ │ │ │ └── web-server.ts
│ │ │ ├── builders/
│ │ │ │ ├── compose.ts
│ │ │ │ ├── docker-file.ts
│ │ │ │ ├── drop.ts
│ │ │ │ ├── heroku.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── nixpacks.ts
│ │ │ │ ├── paketo.ts
│ │ │ │ ├── railpack.ts
│ │ │ │ ├── static.ts
│ │ │ │ └── utils.ts
│ │ │ ├── cluster/
│ │ │ │ └── upload.ts
│ │ │ ├── crons/
│ │ │ │ └── enterprise.ts
│ │ │ ├── databases/
│ │ │ │ ├── mariadb.ts
│ │ │ │ ├── mongo.ts
│ │ │ │ ├── mysql.ts
│ │ │ │ ├── postgres.ts
│ │ │ │ ├── rebuild.ts
│ │ │ │ └── redis.ts
│ │ │ ├── docker/
│ │ │ │ ├── collision/
│ │ │ │ │ └── root-network.ts
│ │ │ │ ├── collision.ts
│ │ │ │ ├── compose/
│ │ │ │ │ ├── configs.ts
│ │ │ │ │ ├── network.ts
│ │ │ │ │ ├── secrets.ts
│ │ │ │ │ ├── service.ts
│ │ │ │ │ └── volume.ts
│ │ │ │ ├── compose.ts
│ │ │ │ ├── domain.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── filesystem/
│ │ │ │ ├── directory.ts
│ │ │ │ └── ssh.ts
│ │ │ ├── gpu-setup.ts
│ │ │ ├── notifications/
│ │ │ │ ├── build-error.ts
│ │ │ │ ├── build-success.ts
│ │ │ │ ├── database-backup.ts
│ │ │ │ ├── docker-cleanup.ts
│ │ │ │ ├── dokploy-restart.ts
│ │ │ │ ├── server-threshold.ts
│ │ │ │ ├── utils.ts
│ │ │ │ └── volume-backup.ts
│ │ │ ├── process/
│ │ │ │ ├── ExecError.ts
│ │ │ │ ├── execAsync.ts
│ │ │ │ └── spawnAsync.ts
│ │ │ ├── providers/
│ │ │ │ ├── bitbucket.ts
│ │ │ │ ├── docker.ts
│ │ │ │ ├── git.ts
│ │ │ │ ├── gitea.ts
│ │ │ │ ├── github.ts
│ │ │ │ ├── gitlab.ts
│ │ │ │ └── raw.ts
│ │ │ ├── restore/
│ │ │ │ ├── compose.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── mariadb.ts
│ │ │ │ ├── mongo.ts
│ │ │ │ ├── mysql.ts
│ │ │ │ ├── postgres.ts
│ │ │ │ ├── utils.ts
│ │ │ │ └── web-server.ts
│ │ │ ├── schedules/
│ │ │ │ ├── index.ts
│ │ │ │ └── utils.ts
│ │ │ ├── servers/
│ │ │ │ └── remote-docker.ts
│ │ │ ├── startup/
│ │ │ │ └── cancell-deployments.ts
│ │ │ ├── tracking/
│ │ │ │ └── hubspot.ts
│ │ │ ├── traefik/
│ │ │ │ ├── application.ts
│ │ │ │ ├── domain.ts
│ │ │ │ ├── file-types.ts
│ │ │ │ ├── middleware.ts
│ │ │ │ ├── redirect.ts
│ │ │ │ ├── security.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── web-server.ts
│ │ │ ├── volume-backups/
│ │ │ │ ├── backup.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── restore.ts
│ │ │ │ └── utils.ts
│ │ │ └── watch-paths/
│ │ │ └── should-deploy.ts
│ │ ├── verification/
│ │ │ └── send-verification-email.tsx
│ │ └── wss/
│ │ └── utils.ts
│ ├── tsconfig.json
│ ├── tsconfig.server.json
│ └── tsconfig.server.no-decl.json
└── pnpm-workspace.yaml
================================================
FILE CONTENTS
================================================
================================================
FILE: .devcontainer/Dockerfile
================================================
# Dockerfile for DevContainer
FROM node:24.4.0-bullseye-slim
# Install essential packages
RUN apt-get update && apt-get install -y \
curl \
bash \
git \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Set up PNPM
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable && corepack prepare pnpm@10.22.0 --activate
# Create workspace directory
WORKDIR /workspaces/dokploy
# Set up user permissions
USER node
================================================
FILE: .devcontainer/devcontainer.json
================================================
{
"name": "Dokploy development container",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"moby": true,
"version": "latest"
},
"ghcr.io/devcontainers/features/git:1": {
"ppa": true,
"version": "latest"
},
"ghcr.io/devcontainers/features/go:1": {
"version": "1.20"
}
},
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.vscode-typescript-next",
"bradlc.vscode-tailwindcss",
"ms-vscode.vscode-json",
"biomejs.biome",
"golang.go",
"redhat.vscode-xml",
"github.vscode-github-actions",
"github.copilot",
"github.copilot-chat"
]
}
},
"forwardPorts": [3000, 5432, 6379],
"portsAttributes": {
"3000": {
"label": "Dokploy App",
"onAutoForward": "notify"
},
"5432": {
"label": "PostgreSQL",
"onAutoForward": "silent"
},
"6379": {
"label": "Redis",
"onAutoForward": "silent"
}
},
"remoteUser": "node",
"workspaceFolder": "/workspaces/dokploy",
"runArgs": ["--name", "dokploy-devcontainer"]
}
================================================
FILE: .dockerignore
================================================
node_modules
.git
.gitignore
*.md
dist
================================================
FILE: .github/CODEOWNERS
================================================
# These owners will be the default owners for everything in
* @siumauricio
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [siumauricio]
patreon: #
open_collective: dokploy
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
description: Create a bug report
labels: ["needs-triage🔍"]
body:
- type: markdown
attributes:
value: |
Before opening a new issue, please do a search of existing issues.
If you need help with your own project, you can start a discussion in the [Q&A Section](https://github.com/Dokploy/dokploy/discussions).
- type: textarea
attributes:
label: To Reproduce
description: |
A detailed, step-by-step description of how to reproduce the issue is required.
Please ensure your report includes clear instructions using numbered lists.
If possible, provide a link to a repository or project where the issue can be reproduced.
placeholder: |
1. Create a application
2. Click X
3. Y will happen
Make sure to:
- Use numbered lists to outline steps clearly.
- Include all relevant commands and configurations.
- Provide a link to a reproducible repository if applicable.
validations:
required: true
- type: textarea
attributes:
label: Current vs. Expected behavior
description: A clear and concise description of what the bug is, and what you expected to happen.
placeholder: "Following the steps from the previous section, I expected A to happen, but I observed B instead"
validations:
required: true
- type: textarea
attributes:
label: Provide environment information
description: Please provide the following information about your environment.
render: bash
placeholder: |
Operating System:
OS: Ubuntu 20.04
Arch: arm64
Dokploy version: 0.2.2'
VPS Provider: DigitalOcean, Hetzner, Linode, etc.
What applications/services are you tying to deploy?
eg - Database, Nextjs App, laravel, etc.
validations:
required: true
- type: dropdown
attributes:
label: Which area(s) are affected? (Select all that apply)
multiple: true
options:
- "Installation"
- "Application"
- "Databases"
- "Docker Compose"
- "Traefik"
- "Docker"
- "Remote server"
- "Local Development"
- "Cloud Version"
validations:
required: true
- type: dropdown
attributes:
label: Are you deploying the applications where Dokploy is installed or on a remote server?
options:
- "Same server where Dokploy is installed"
- "Remote server"
- "Both"
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: |
Any extra information that might help us investigate.
placeholder: |
I tested on a DigitalOcean VPS with Ubuntu 20.04 and Docker version 20.10.12.
- type: dropdown
attributes:
label: Will you send a PR to fix it?
description: Let us know if you are planning to submit a pull request to address this issue.
options:
- "Yes"
- "No"
- "Maybe, need help"
validations:
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Questions?
url: https://github.com/Dokploy/dokploy/discussions
about: Ask your questions here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.yml
================================================
name: Feature Request
description: Suggest a new feature or improvement to the project
labels: ["enhancement"]
body:
- type: textarea
attributes:
label: What problem will this feature address?
description: A clear and concise description of what the problem is.
placeholder: |
I'm always frustrated when I can't do X
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
placeholder: Add X to the core
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
placeholder: |
Maybe use Y as a workaround?
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
validations:
required: false
- type: dropdown
attributes:
label: Will you send a PR to implement it?
description: Let us know if you are planning to submit a pull request to implement this feature.
options:
- "Yes"
- "No"
- "Maybe, need help"
validations:
required: true
================================================
FILE: .github/pull_request_template.md
================================================
## What is this PR about?
Please describe in a short paragraph what this PR is about.
## Checklist
Before submitting this PR, please make sure that:
- [ ] You created a dedicated branch based on the `canary` branch.
- [ ] You have read the suggestions in the CONTRIBUTING.md file https://github.com/Dokploy/dokploy/blob/canary/CONTRIBUTING.md#pull-request
- [ ] You have tested this PR in your local instance. If you have not tested it yet, please do so before submitting. This helps avoid wasting maintainers' time reviewing code that has not been verified by you.
## Issues related (if applicable)
closes #123
## Screenshots (if applicable)
================================================
FILE: .github/workflows/create-pr.yml
================================================
name: Auto PR to main when version changes
on:
push:
branches:
- canary
permissions:
contents: write
pull-requests: write
jobs:
create-pr:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get version from package.json
run: echo "VERSION=$(jq -r .version ./apps/dokploy/package.json)" >> $GITHUB_ENV
- name: Get latest GitHub tag
run: |
LATEST_TAG=$(git ls-remote --tags origin | awk -F'/' '{print $3}' | sort -V | tail -n1)
echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV
echo $LATEST_TAG
- name: Compare versions
run: |
if [ "${{ env.VERSION }}" != "${{ env.LATEST_TAG }}" ]; then
VERSION_CHANGED="true"
else
VERSION_CHANGED="false"
fi
echo "VERSION_CHANGED=$VERSION_CHANGED" >> $GITHUB_ENV
echo "Comparing versions:"
echo "Current version: ${{ env.VERSION }}"
echo "Latest tag: ${{ env.LATEST_TAG }}"
echo "Version changed: $VERSION_CHANGED"
- name: Check if a PR already exists
run: |
PR_EXISTS=$(gh pr list --state open --base main --head canary --json number --jq '. | length')
echo "PR_EXISTS=$PR_EXISTS" >> $GITHUB_ENV
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
- name: Create Pull Request
if: env.VERSION_CHANGED == 'true' && env.PR_EXISTS == '0'
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git fetch origin main
git checkout canary
git push origin canary
gh pr create \
--title "🚀 Release ${{ env.VERSION }}" \
--body '
This PR promotes changes from `canary` to `main` for version ${{ env.VERSION }}.
### 🔍 Changes Include:
- Version bump to ${{ env.VERSION }}
- All changes from canary branch
### ✅ Pre-merge Checklist:
- [ ] All tests passing
- [ ] Documentation updated
- [ ] Docker images built and tested
> 🤖 This PR was automatically generated by [GitHub Actions](https://github.com/actions)' \
--base main \
--head canary \
--label "release" --label "automated pr" || true \
--reviewer siumauricio \
--assignee siumauricio
env:
GH_TOKEN: ${{ github.token }}
================================================
FILE: .github/workflows/deploy.yml
================================================
name: Build Docker images
on:
push:
branches: [main, canary]
workflow_dispatch:
jobs:
build-and-push-cloud-image:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set tag and version
id: meta-cloud
run: |
VERSION=$(jq -r .version apps/dokploy/package.json)
echo "version=$VERSION" >> $GITHUB_OUTPUT
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "tags=siumauricio/cloud:latest,siumauricio/cloud:${VERSION}" >> $GITHUB_OUTPUT
else
echo "tags=siumauricio/cloud:canary" >> $GITHUB_OUTPUT
fi
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.cloud
push: true
tags: ${{ steps.meta-cloud.outputs.tags }}
platforms: linux/amd64
build-args: |
NEXT_PUBLIC_UMAMI_HOST=${{ secrets.NEXT_PUBLIC_UMAMI_HOST }}
NEXT_PUBLIC_UMAMI_WEBSITE_ID=${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${{ secrets.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY }}
build-and-push-schedule-image:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set tag and version
id: meta-schedule
run: |
VERSION=$(jq -r .version apps/dokploy/package.json)
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "tags=siumauricio/schedule:latest,siumauricio/schedule:${VERSION}" >> $GITHUB_OUTPUT
else
echo "tags=siumauricio/schedule:canary" >> $GITHUB_OUTPUT
fi
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.schedule
push: true
tags: ${{ steps.meta-schedule.outputs.tags }}
platforms: linux/amd64
build-and-push-server-image:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set tag and version
id: meta-server
run: |
VERSION=$(jq -r .version apps/dokploy/package.json)
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "tags=siumauricio/server:latest,siumauricio/server:${VERSION}" >> $GITHUB_OUTPUT
else
echo "tags=siumauricio/server:canary" >> $GITHUB_OUTPUT
fi
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile.server
push: true
tags: ${{ steps.meta-server.outputs.tags }}
platforms: linux/amd64
================================================
FILE: .github/workflows/dokploy.yml
================================================
name: Dokploy Docker Build
on:
push:
branches: [main, canary, "fix/re-apply-database-migration-fix"]
workflow_dispatch:
env:
IMAGE_NAME: dokploy/dokploy
jobs:
docker-amd:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set tag and version
id: meta
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
TAG="latest"
VERSION=$(node -p "require('./apps/dokploy/package.json').version")
elif [ "${{ github.ref }}" = "refs/heads/canary" ]; then
TAG="canary"
else
TAG="feature"
fi
echo "tags=${IMAGE_NAME}:${TAG}-amd64" >> $GITHUB_OUTPUT
- name: Prepare env file
run: |
cp apps/dokploy/.env.production.example .env.production
cp apps/dokploy/.env.production.example apps/dokploy/.env.production
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
docker-arm:
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set tag and version
id: meta
run: |
VERSION=$(node -p "require('./apps/dokploy/package.json').version")
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
TAG="latest"
VERSION=$(node -p "require('./apps/dokploy/package.json').version")
elif [ "${{ github.ref }}" = "refs/heads/canary" ]; then
TAG="canary"
else
TAG="feature"
fi
echo "tags=${IMAGE_NAME}:${TAG}-arm64" >> $GITHUB_OUTPUT
- name: Prepare env file
run: |
cp apps/dokploy/.env.production.example .env.production
cp apps/dokploy/.env.production.example apps/dokploy/.env.production
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
combine-manifests:
needs: [docker-amd, docker-arm]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Create and push manifests
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
VERSION=$(node -p "require('./apps/dokploy/package.json').version")
TAG="latest"
docker buildx imagetools create -t ${IMAGE_NAME}:${TAG} \
${IMAGE_NAME}:${TAG}-amd64 \
${IMAGE_NAME}:${TAG}-arm64
docker buildx imagetools create -t ${IMAGE_NAME}:${VERSION} \
${IMAGE_NAME}:${TAG}-amd64 \
${IMAGE_NAME}:${TAG}-arm64
elif [ "${{ github.ref }}" = "refs/heads/canary" ]; then
TAG="canary"
docker buildx imagetools create -t ${IMAGE_NAME}:${TAG} \
${IMAGE_NAME}:${TAG}-amd64 \
${IMAGE_NAME}:${TAG}-arm64
else
TAG="feature"
docker buildx imagetools create -t ${IMAGE_NAME}:${TAG} \
${IMAGE_NAME}:${TAG}-amd64 \
${IMAGE_NAME}:${TAG}-arm64
fi
generate-release:
needs: [combine-manifests]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get version
id: get_version
run: |
VERSION=$(node -p "require('./apps/dokploy/package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.get_version.outputs.version }}
name: ${{ steps.get_version.outputs.version }}
generate_release_notes: true
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/format.yml
================================================
name: autofix.ci
on:
push:
branches: [canary]
pull_request:
branches: [canary]
jobs:
format:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup biomeJs
uses: biomejs/setup-biome@v2
- name: Run Biome formatter
run: biome format --write
- uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27 # v1.3.2
================================================
FILE: .github/workflows/monitoring.yml
================================================
name: Dokploy Monitoring Build
on:
push:
branches: [main, canary]
env:
IMAGE_NAME: dokploy/monitoring
jobs:
docker-amd:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set tag
id: meta
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
TAG="latest"
elif [ "${{ github.ref }}" = "refs/heads/canary" ]; then
TAG="canary"
else
TAG="feature"
fi
echo "tags=${IMAGE_NAME}:${TAG}-amd64" >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.monitoring
platforms: linux/amd64
push: true
tags: ${{ steps.meta.outputs.tags }}
docker-arm:
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set
id: meta
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
TAG="latest"
elif [ "${{ github.ref }}" = "refs/heads/canary" ]; then
TAG="canary"
else
TAG="feature"
fi
echo "tags=${IMAGE_NAME}:${TAG}-arm64" >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.monitoring
platforms: linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
combine-manifests:
needs: [docker-amd, docker-arm]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Create and push manifests
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
TAG="latest"
docker buildx imagetools create -t ${IMAGE_NAME}:${TAG} \
${IMAGE_NAME}:${TAG}-amd64 \
${IMAGE_NAME}:${TAG}-arm64
elif [ "${{ github.ref }}" = "refs/heads/canary" ]; then
TAG="canary"
docker buildx imagetools create -t ${IMAGE_NAME}:${TAG} \
${IMAGE_NAME}:${TAG}-amd64 \
${IMAGE_NAME}:${TAG}-arm64
else
TAG="feature"
docker buildx imagetools create -t ${IMAGE_NAME}:${TAG} \
${IMAGE_NAME}:${TAG}-amd64 \
${IMAGE_NAME}:${TAG}-arm64
fi
================================================
FILE: .github/workflows/pr-quality.yml
================================================
name: PR Quality
permissions:
contents: read
issues: read
pull-requests: write
on:
pull_request_target:
types: [opened, reopened]
jobs:
anti-slop:
runs-on: ubuntu-latest
steps:
- uses: peakoss/anti-slop@v0
with:
max-failures: 4
blocked-commit-authors: "claude,copilot"
require-description: true
min-account-age: 5
================================================
FILE: .github/workflows/pull-request.yml
================================================
name: Pull Request
on:
pull_request:
branches: [main, canary]
permissions:
contents: read
jobs:
pr-check:
runs-on: ubuntu-latest
strategy:
matrix:
job: [build, test, typecheck]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 24.4.0
cache: "pnpm"
- name: Install Nixpacks
if: matrix.job == 'test'
run: |
export NIXPACKS_VERSION=1.41.0
curl -sSL https://nixpacks.com/install.sh | bash
echo "Nixpacks installed $NIXPACKS_VERSION"
- name: Install Railpack
if: matrix.job == 'test'
run: |
export RAILPACK_VERSION=0.15.4
curl -sSL https://railpack.com/install.sh | bash
echo "Railpack installed $RAILPACK_VERSION"
- name: Add build tools to PATH
if: matrix.job == 'test'
run: echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Initialize Docker Swarm
if: matrix.job == 'test'
run: |
docker swarm init
docker network create --driver overlay dokploy-network || true
echo "✅ Docker Swarm initialized"
- run: pnpm install --frozen-lockfile
- run: pnpm server:build
- run: pnpm ${{ matrix.job }}
================================================
FILE: .github/workflows/sync-openapi-docs.yml
================================================
name: Generate and Sync OpenAPI
on:
push:
branches:
- canary
- main
paths:
- 'apps/dokploy/server/api/routers/**'
- 'packages/server/src/services/**'
- 'packages/server/src/db/schema/**'
workflow_dispatch:
jobs:
generate-and-commit:
name: Generate OpenAPI and commit to Dokploy repo
runs-on: ubuntu-latest
steps:
- name: Checkout Dokploy repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 24.4.0
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Generate OpenAPI specification
run: |
pnpm generate:openapi
# Verifica que se generó correctamente
if [ ! -f openapi.json ]; then
echo "❌ openapi.json not found"
exit 1
fi
echo "✅ OpenAPI specification generated successfully"
- name: Sync to website repository
run: |
# Clona el repositorio de website
git clone https://x-access-token:${{ secrets.DOCS_SYNC_TOKEN }}@github.com/dokploy/website.git website-repo
cd website-repo
# Copia el openapi.json al website (sobrescribe)
mkdir -p apps/docs/public
cp -f ../openapi.json apps/docs/public/openapi.json
# Configura git
git config user.name "Dokploy Bot"
git config user.email "bot@dokploy.com"
# Agrega y commitea siempre
git add apps/docs/public/openapi.json
git commit -m "chore: sync OpenAPI specification [skip ci]" \
-m "Source: ${{ github.repository }}@${{ github.sha }}" \
-m "Updated: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" \
--allow-empty
git push
echo "✅ OpenAPI synced to website successfully"
================================================
FILE: .gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Dependencies
node_modules
.pnp
.pnp.js
.docker
# Local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
openapi.json
# Testing
coverage
# Turbo
.turbo
# Vercel
.vercel
# Build Outputs
.next/
out/
dist
# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor
.idea
# Misc
.DS_Store
*.pem
.db
================================================
FILE: .nvmrc
================================================
24.4.0
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": ["biomejs.biome"]
}
================================================
FILE: .vscode/settings.json
================================================
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "biomejs.biome",
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
}
}
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
Hey, thanks for your interest in contributing to Dokploy! We appreciate your help and taking your time to contribute.
Before you start, please first discuss the feature/bug you want to add with the owners and community via github issues.
We have a few guidelines to follow when contributing to this project:
- [Commit Convention](#commit-convention)
- [Setup](#setup)
- [Development](#development)
- [Build](#build)
- [Pull Request](#pull-request)
- [Important Considerations](#important-considerations-for-pull-requests)
## Commit Convention
Before you create a Pull Request, please make sure your commit message follows the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
### Commit Message Format
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
#### Type
Must be one of the following:
- **feat**: A new feature
- **fix**: A bug fix
- **docs**: Documentation only changes
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
- **refactor**: A code change that neither fixes a bug nor adds a feature
- **perf**: A code change that improves performance
- **test**: Adding missing tests or correcting existing tests
- **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
- **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
- **chore**: Other changes that don't modify `src` or `test` files
- **revert**: Reverts a previous commit
Example:
```
feat: add new feature
```
## Setup
Before you start, please make the clone based on the `canary` branch, since the `main` branch is the source of truth and should always reflect the latest stable release, also the PRs will be merged to the `canary` branch.
We use Node v24.4.0 and recommend this specific version. If you have nvm installed, you can run `nvm install 24.4.0 && nvm use` in the root directory.
```bash
git clone https://github.com/dokploy/dokploy.git
cd dokploy
pnpm install
cp apps/dokploy/.env.example apps/dokploy/.env
```
## Requirements
- [Docker](/GUIDES.md#docker)
### Setup
Run the command that will spin up all the required services and files.
```bash
pnpm run dokploy:setup
```
Run this script
```bash
pnpm run server:script
```
Now run the development server.
```bash
pnpm run dokploy:dev
```
Go to http://localhost:3000 to see the development server
> [!NOTE]
> This project uses Biome. If your editor is configured to use another formatter such as Prettier, it's recommended to either change it to use Biome or turn it off.
## Build
```bash
pnpm run dokploy:build
```
## Docker
To build the docker image
```bash
pnpm run docker:build
```
To push the docker image
```bash
pnpm run docker:push
```
## Password Reset
In the case you lost your password, you can reset it using the following command
```bash
pnpm run reset-password
```
If you want to test the webhooks on development mode using localtunnel, make sure to install [`localtunnel`](https://localtunnel.app/)
```bash
pnpm dlx localtunnel --port 3000
```
If you run into permission issues of docker run the following command
```bash
sudo chown -R USERNAME dokploy or sudo chown -R $(whoami) ~/.docker
```
## Application deploy
In case you want to deploy the application on your machine and you selected nixpacks or buildpacks, you need to install first.
```bash
# Install Nixpacks
curl -sSL https://nixpacks.com/install.sh -o install.sh \
&& chmod +x install.sh \
&& ./install.sh
```
```bash
# Install Railpack
curl -sSL https://railpack.com/install.sh | sh
```
```bash
# Install Buildpacks
curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.39.1/pack-v0.39.1-linux.tgz" | tar -C /usr/local/bin/ --no-same-owner -xzv pack
```
## Pull Request
- The `canary` branch is the source of truth and should always reflect the latest stable release.
- Create a new branch for each feature or bug fix.
- Make sure to add tests for your changes.
- Make sure to update the documentation for any changes Go to the [docs.dokploy.com](https://docs.dokploy.com) website to see the changes.
- When creating a pull request, please provide a clear and concise description of the changes made.
- If you include a video or screenshot, would be awesome so we can see the changes in action.
- If your pull request fixes an open issue, please reference the issue in the pull request description.
- Once your pull request is merged, you will be automatically added as a contributor to the project.
### Important Considerations for Pull Requests
- **Testing is Mandatory:** All Pull Requests **must be tested** by the PR author before submission. You must verify that your changes work as expected in a local development environment (see [Setup](#setup)). **Pull Requests that have not been tested by their creator will be rejected.** This policy keeps the PR history clean and values contributors who submit verified, working code. Untested PRs are often recognizable by disproportionately large or scattered changes for simple tasks—please test first.
- **Focus and Scope:** Each Pull Request should ideally address a single, well-defined problem or introduce one new feature. This greatly facilitates review and reduces the chances of introducing unintended side effects.
- **Avoid Unfocused Changes:** Please avoid submitting Pull Requests that contain only minor changes such as whitespace adjustments, IDE-generated formatting, or removal of unused variables, unless these are part of a larger, clearly defined refactor or a dedicated "cleanup" Pull Request that addresses a specific `good first issue` or maintenance task.
- **Issue Association:** For any significant change, it's highly recommended to open an issue first to discuss the proposed solution with the community and maintainers. This ensures alignment and avoids duplicated effort. If your PR resolves an existing issue, please link it in the description (e.g., `Fixes #123`, `Closes #456`).
- **Large Features:** Pull Requests that introduce very large or broad features **will not be accepted** unless the idea is first outlined and discussed in a GitHub issue. Large features should be designed together with the Dokploy team so the project stays coherent and moves in the same direction. Open an issue to propose and align on the design before implementing.
Thank you for your contribution!
## Templates
To add a new template, go to `https://github.com/Dokploy/templates` repository and read the README.md file.
### Recommendations
- Use the same name of the folder as the id of the template.
- The logo should be in the public folder.
- If you want to show a domain in the UI, please add the `_HOST` suffix at the end of the variable name.
- Test first on a vps or a server to make sure the template works.
## Docs & Website
To contribute to the Dokploy docs or website, please go to this [repository](https://github.com/Dokploy/website).
================================================
FILE: Dockerfile
================================================
# syntax=docker/dockerfile:1
FROM node:24.4.0-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
RUN corepack prepare pnpm@10.22.0 --activate
FROM base AS build
COPY . /usr/src/app
WORKDIR /usr/src/app
RUN apt-get update && apt-get install -y python3 make g++ git python3-pip pkg-config libsecret-1-dev && rm -rf /var/lib/apt/lists/*
# Install dependencies
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
# Deploy only the dokploy app
ENV NODE_ENV=production
RUN pnpm --filter=@dokploy/server build
RUN pnpm --filter=./apps/dokploy run build
RUN pnpm --filter=./apps/dokploy --prod deploy --legacy /prod/dokploy
RUN cp -R /usr/src/app/apps/dokploy/.next /prod/dokploy/.next
RUN cp -R /usr/src/app/apps/dokploy/dist /prod/dokploy/dist
FROM base AS dokploy
WORKDIR /app
# Set production
ENV NODE_ENV=production
RUN apt-get update && apt-get install -y curl unzip zip apache2-utils iproute2 rsync git-lfs && git lfs install && rm -rf /var/lib/apt/lists/*
# Copy only the necessary files
COPY --from=build /prod/dokploy/.next ./.next
COPY --from=build /prod/dokploy/dist ./dist
COPY --from=build /prod/dokploy/next.config.mjs ./next.config.mjs
COPY --from=build /prod/dokploy/public ./public
COPY --from=build /prod/dokploy/package.json ./package.json
COPY --from=build /prod/dokploy/drizzle ./drizzle
COPY .env.production ./.env
COPY --from=build /prod/dokploy/components.json ./components.json
COPY --from=build /prod/dokploy/node_modules ./node_modules
# Install docker
RUN curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh --version 28.5.2 && rm get-docker.sh && curl https://rclone.org/install.sh | bash
# Install Nixpacks and tsx
# | VERBOSE=1 VERSION=1.21.0 bash
ARG NIXPACKS_VERSION=1.41.0
RUN curl -sSL https://nixpacks.com/install.sh -o install.sh \
&& chmod +x install.sh \
&& ./install.sh \
&& pnpm install -g tsx
# Install Railpack
ARG RAILPACK_VERSION=0.15.4
RUN curl -sSL https://railpack.com/install.sh | bash
# Install buildpacks
COPY --from=buildpacksio/pack:0.39.1 /usr/local/bin/pack /usr/local/bin/pack
EXPOSE 3000
HEALTHCHECK --interval=10s --timeout=3s --retries=10 \
CMD curl -fs http://localhost:3000/api/trpc/settings.health || exit 1
CMD ["sh", "-c", "pnpm run wait-for-postgres && exec pnpm start"]
================================================
FILE: Dockerfile.cloud
================================================
# syntax=docker/dockerfile:1
FROM node:24.4.0-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
RUN corepack prepare pnpm@10.22.0 --activate
FROM base AS build
COPY . /usr/src/app
WORKDIR /usr/src/app
RUN apt-get update && apt-get install -y python3 make g++ git python3-pip pkg-config libsecret-1-dev && rm -rf /var/lib/apt/lists/*
# Install dependencies
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm --filter=@dokploy/server --filter=./apps/dokploy install --frozen-lockfile
# Deploy only the dokploy app
# ARG NEXT_PUBLIC_UMAMI_HOST
# ENV NEXT_PUBLIC_UMAMI_HOST=$NEXT_PUBLIC_UMAMI_HOST
# ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
# ENV NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID
ARG NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
ENV NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=$NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
ENV NODE_ENV=production
RUN pnpm --filter=@dokploy/server build
RUN pnpm --filter=./apps/dokploy run build
RUN pnpm --filter=./apps/dokploy --prod deploy --legacy /prod/dokploy
RUN cp -R /usr/src/app/apps/dokploy/.next /prod/dokploy/.next
RUN cp -R /usr/src/app/apps/dokploy/dist /prod/dokploy/dist
FROM base AS dokploy
WORKDIR /app
# Set production
ENV NODE_ENV=production
RUN apt-get update && apt-get install -y curl unzip apache2-utils && rm -rf /var/lib/apt/lists/*
# Copy only the necessary files
COPY --from=build /prod/dokploy/.next ./.next
COPY --from=build /prod/dokploy/dist ./dist
COPY --from=build /prod/dokploy/next.config.mjs ./next.config.mjs
COPY --from=build /prod/dokploy/public ./public
COPY --from=build /prod/dokploy/package.json ./package.json
COPY --from=build /prod/dokploy/drizzle ./drizzle
COPY --from=build /prod/dokploy/components.json ./components.json
COPY --from=build /prod/dokploy/node_modules ./node_modules
# Install RCLONE
RUN curl https://rclone.org/install.sh | bash
# tsx
RUN pnpm install -g tsx
EXPOSE 3000
CMD [ "pnpm", "start" ]
================================================
FILE: Dockerfile.monitoring
================================================
# syntax=docker/dockerfile:1
# Build stage
FROM golang:1.21-alpine3.19 AS builder
# Instalar dependencias necesarias
RUN apk add --no-cache gcc musl-dev sqlite-dev
# Establecer el directorio de trabajo
WORKDIR /app
# Copiar todo el código fuente primero
COPY . .
# Movernos al directorio de la aplicación golang
WORKDIR /app/apps/monitoring
# Descargar dependencias
RUN go mod download
# Compilar la aplicación
RUN CGO_ENABLED=1 GOOS=linux go build -o main main.go
# Etapa final
FROM alpine:3.19
# Instalar SQLite y otras dependencias necesarias
RUN apk add --no-cache sqlite-libs docker-cli
WORKDIR /app
# Copiar el binario compilado y el archivo monitor.go
COPY --from=builder /app/apps/monitoring/main ./main
COPY --from=builder /app/apps/monitoring/main.go ./monitor.go
# COPY --from=builder /app/apps/golang/.env ./.env
# Exponer el puerto
ENV PORT=3001
EXPOSE 3001
# Ejecutar la aplicación
CMD ["./main"]
================================================
FILE: Dockerfile.schedule
================================================
# syntax=docker/dockerfile:1
FROM node:24.4.0-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
RUN corepack prepare pnpm@10.22.0 --activate
FROM base AS build
COPY . /usr/src/app
WORKDIR /usr/src/app
RUN apt-get update && apt-get install -y python3 make g++ git python3-pip pkg-config libsecret-1-dev && rm -rf /var/lib/apt/lists/*
# Install dependencies
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm --filter=@dokploy/server --filter=./apps/schedules install --frozen-lockfile
# Deploy only the dokploy app
ENV NODE_ENV=production
RUN pnpm --filter=@dokploy/server build
RUN pnpm --filter=./apps/schedules run build
RUN pnpm --filter=./apps/schedules --prod deploy --legacy /prod/schedules
RUN cp -R /usr/src/app/apps/schedules/dist /prod/schedules/dist
FROM base AS dokploy
WORKDIR /app
# Set production
ENV NODE_ENV=production
# Copy only the necessary files
COPY --from=build /prod/schedules/dist ./dist
COPY --from=build /prod/schedules/package.json ./package.json
COPY --from=build /prod/schedules/node_modules ./node_modules
ENV HOSTNAME=0.0.0.0
CMD ["pnpm", "start"]
================================================
FILE: Dockerfile.server
================================================
# syntax=docker/dockerfile:1
FROM node:24.4.0-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
RUN corepack prepare pnpm@10.22.0 --activate
FROM base AS build
COPY . /usr/src/app
WORKDIR /usr/src/app
RUN apt-get update && apt-get install -y python3 make g++ git python3-pip pkg-config libsecret-1-dev && rm -rf /var/lib/apt/lists/*
# Install dependencies
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm --filter=@dokploy/server --filter=./apps/api install --frozen-lockfile
# Deploy only the dokploy app
ENV NODE_ENV=production
RUN pnpm --filter=@dokploy/server build
RUN pnpm --filter=./apps/api run build
RUN pnpm --filter=./apps/api --prod deploy --legacy /prod/api
RUN cp -R /usr/src/app/apps/api/dist /prod/api/dist
FROM base AS dokploy
WORKDIR /app
# Set production
ENV NODE_ENV=production
# Copy only the necessary files
COPY --from=build /prod/api/dist ./dist
COPY --from=build /prod/api/package.json ./package.json
COPY --from=build /prod/api/node_modules ./node_modules
ENV HOSTNAME=0.0.0.0
CMD ["pnpm", "start"]
================================================
FILE: GUIDES.md
================================================
# Docker
Here's how to install docker on different operating systems:
## macOS
1. Visit [Docker Desktop for Mac](https://www.docker.com/products/docker-desktop)
2. Download the Docker Desktop installer
3. Double-click the downloaded `.dmg` file
4. Drag Docker to your Applications folder
5. Open Docker Desktop from Applications
6. Follow the onboarding tutorial if desired
## Linux
### Ubuntu
```bash
# Uninstall old versions
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
# Update package index
sudo apt-get update
# Install prerequisites
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
# Add Docker's official GPG key
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```
## Windows
1. Enable WSL2 if not already enabled
2. Visit [Docker Desktop for Windows](https://www.docker.com/products/docker-desktop)
3. Download the installer
4. Run the installer and follow the prompts
5. Start Docker Desktop from the Start menu
================================================
FILE: LICENSE.MD
================================================
Copyright 2026-present Dokploy Technology, Inc.
Portions of this software are licensed as follows:
* All content that resides under a "/proprietary" directory of this repository, if that directory exists, is licensed under the license defined in "LICENSE_PROPRIETARY".
* Content outside of the above mentioned directories or restrictions above is available under the "Apache License 2.0" license as defined below.
## Apache License 2.0
Copyright 2026-present Dokploy Technology, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
================================================
FILE: LICENSE_PROPRIETARY.md
================================================
The Dokploy Source Available license (DSAL) version 1.0
Copyright (c) 2026-present Dokploy Technology, Inc.
With regard to the Dokploy Software:This software and associated documentation files (the "Software") may only beused in production, if you (and any entity that you represent) have agreed to, and are in compliance with, a valid commercial agreement from Dokploy.Subject to the foregoing sentence, you are free to modify this Software and publish patches to the Software. You agree that Dokploy and/or its licensors (as applicable) retain all right, title and interest in and to all such modifications and/or patches, and all such modifications and/or patches may only be used, copied, modified, displayed, distributed, or otherwise exploited with a valid Dokploy Source Available License. Notwithstanding the foregoing, you may copy and modify the Software for development and testing purposes, without requiring a subscription. You agree that Dokploy and/or its licensors (as applicable) retain all right, title and interest in and to all such modifications. You are not granted any other rights beyond what is expressly stated herein. Subject to theforegoing, it is forbidden to copy, merge, publish, distribute, sublicense,and/or sell the Software.
This Dokploy Source Available license applies only to the part of this Software that is in a /proprietary folder. The full text of this License shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THESOFTWARE.
For all third party components incorporated into the Dokploy Software, thosecomponents are licensed under the original license provided by the owner of the applicable component.
================================================
FILE: README.md
================================================
<div align="center">
<a href="https://dokploy.com">
<img src=".github/sponsors/logo.png" alt="Dokploy - Open Source Alternative to Vercel, Heroku and Netlify." width="100%" />
</a>
</br>
</br>
<p>Join us on Discord for help, feedback, and discussions!</p>
<a href="https://discord.gg/2tBnJ3jDJc">
<img src="https://discordapp.com/api/guilds/1234073262418563112/widget.png?style=banner2" alt="Discord Shield"/>
</a>
</div>
<br />
Dokploy is a free, self-hostable Platform as a Service (PaaS) that simplifies the deployment and management of applications and databases.
## ✨ Features
Dokploy includes multiple features to make your life easier.
- **Applications**: Deploy any type of application (Node.js, PHP, Python, Go, Ruby, etc.).
- **Databases**: Create and manage databases with support for MySQL, PostgreSQL, MongoDB, MariaDB, and Redis.
- **Backups**: Automate backups for databases to an external storage destination.
- **Docker Compose**: Native support for Docker Compose to manage complex applications.
- **Multi Node**: Scale applications to multiple nodes using Docker Swarm to manage the cluster.
- **Templates**: Deploy open-source templates (Plausible, Pocketbase, Calcom, etc.) with a single click.
- **Traefik Integration**: Automatically integrates with Traefik for routing and load balancing.
- **Real-time Monitoring**: Monitor CPU, memory, storage, and network usage for every resource.
- **Docker Management**: Easily deploy and manage Docker containers.
- **CLI/API**: Manage your applications and databases using the command line or through the API.
- **Notifications**: Get notified when your deployments succeed or fail (via Slack, Discord, Telegram, Email, etc.).
- **Multi Server**: Deploy and manage your applications remotely to external servers.
- **Self-Hosted**: Self-host Dokploy on your VPS.
## 🚀 Getting Started
To get started, run the following command on a VPS:
Want to skip the installation process? [Try the Dokploy Cloud](https://app.dokploy.com).
```bash
curl -sSL https://dokploy.com/install.sh | sh
```
For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
[Github Sponsors](https://github.com/sponsors/Siumauricio)
### Contributors 🤝
<a href="https://github.com/dokploy/dokploy/graphs/contributors">
<img src="https://contrib.rocks/image?repo=dokploy/dokploy" alt="Contributors" />
</a>
## 📺 Video Tutorial
<a href="https://youtu.be/mznYKPvhcfw">
<img src="https://dokploy.com/banner.png" alt="Watch the video" width="400"/>
</a>
## 🤝 Contributing
Check out the [Contributing Guide](CONTRIBUTING.md) for more information.
================================================
FILE: SECURITY.md
================================================
# Dokploy Security Policy
At Dokploy, security is a top priority. We appreciate the help of security researchers and the community in identifying and reporting vulnerabilities.
## How to Report a Vulnerability
If you have discovered a security vulnerability in Dokploy, we ask that you report it responsibly by following these guidelines:
1. **Contact us:** Send an email to [contact@dokploy.com](mailto:contact@dokploy.com).
2. **Provide clear details:** Include as much information as possible to help us understand and reproduce the vulnerability. This should include:
* A clear description of the vulnerability.
* Steps to reproduce the vulnerability.
* Any sample code, screenshots, or videos that might be helpful.
* The potential impact of the vulnerability.
3. **Do not make the vulnerability public:** Please refrain from publicly disclosing the vulnerability until we have had the opportunity to investigate and address it. This is crucial for protecting our users.
4. **Allow us time:** We will endeavor to acknowledge receipt of your report as soon as possible and keep you informed of our progress. The time to resolve the vulnerability may vary depending on its complexity and severity.
## What We Expect From You
* Do not access user data or systems beyond what is necessary to demonstrate the vulnerability.
* Do not perform denial-of-service (DoS) attacks, spamming, or social engineering.
* Do not modify or destroy data that does not belong to you.
## Our Commitment
We are committed to working with you quickly and responsibly to address any legitimate security vulnerability.
Thank you for helping us keep Dokploy secure for everyone.
================================================
FILE: TERMS_AND_CONDITIONS.md
================================================
# Terms & Conditions
**Dokploy core** is a free and open-source solution intended as an alternative to established cloud platforms like Vercel and Netlify.
The Dokploy team endeavors to mitigate potential defects and issues through stringent testing and adherence to principles of clean coding. Dokploy is provided "AS IS" without any warranties, express or implied. Refer to the [License](https://github.com/Dokploy/Dokploy/blob/main/LICENSE) for details on permissions and restrictions.
### Description of Service:
**Dokploy core** is an open-source tool designed to simplify the deployment of applications for both personal and business use. Users are permitted to install, modify, and operate Dokploy independently or within their organizations to improve their development and deployment operations. It is important to note that any commercial resale or redistribution of Dokploy as a service is strictly forbidden without explicit consent. This prohibition ensures the preservation of Dokploy's open-source character for the benefit of the entire community.
### Our Responsibility
The Dokploy development team commits to maintaining the functionality of the software and addressing major issues promptly. While we welcome suggestions for new features, the decision to include them rests solely with the core developers of Dokploy.
### Usage Data
**Dokploy** does not collect any user data. It is distributed as a free and open-source tool under the terms of "AS IS", without any implied warranties or conditions.
### Future Changes
The Terms of Service and Terms & Conditions are subject to change without prior notice.
================================================
FILE: apps/api/.gitignore
================================================
# dev
.yarn/
!.yarn/releases
.vscode/*
!.vscode/launch.json
!.vscode/*.code-snippets
.idea/workspace.xml
.idea/usage.statistics.xml
.idea/shelf
# deps
node_modules/
# env
.env
.env.production
# logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# misc
.DS_Store
================================================
FILE: apps/api/README.md
================================================
```
npm install
npm run dev
```
```
open http://localhost:3000
```
================================================
FILE: apps/api/package.json
================================================
{
"name": "@dokploy/api",
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "PORT=4000 tsx watch src/index.ts",
"build": "rimraf dist && tsc --project tsconfig.json",
"start": "node dist/index.js",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"inngest": "3.40.1",
"@dokploy/server": "workspace:*",
"@hono/node-server": "^1.14.3",
"@hono/zod-validator": "0.7.6",
"dotenv": "^16.4.5",
"hono": "^4.11.7",
"pino": "9.4.0",
"pino-pretty": "11.2.2",
"react": "18.2.0",
"react-dom": "18.2.0",
"redis": "4.7.0",
"zod": "^4.3.6"
},
"devDependencies": {
"@types/node": "^24.4.0",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"rimraf": "6.1.3",
"tsx": "^4.16.2",
"typescript": "^5.8.3"
},
"packageManager": "pnpm@10.22.0",
"engines": {
"node": "^24.4.0",
"pnpm": ">=10.22.0"
}
}
================================================
FILE: apps/api/src/index.ts
================================================
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import "dotenv/config";
import { zValidator } from "@hono/zod-validator";
import { Inngest } from "inngest";
import { serve as serveInngest } from "inngest/hono";
import { logger } from "./logger.js";
import {
cancelDeploymentSchema,
type DeployJob,
deployJobSchema,
} from "./schema.js";
import { fetchDeploymentJobs } from "./service.js";
import { deploy } from "./utils.js";
const app = new Hono();
// Initialize Inngest client
export const inngest = new Inngest({
id: "dokploy-deployments",
name: "Dokploy Deployment Service",
});
export const deploymentFunction = inngest.createFunction(
{
id: "deploy-application",
name: "Deploy Application",
concurrency: [
{
key: "event.data.serverId",
limit: 1,
},
],
retries: 0,
cancelOn: [
{
event: "deployment/cancelled",
if: "async.data.applicationId == event.data.applicationId || async.data.composeId == event.data.composeId",
timeout: "1h", // Allow cancellation for up to 1 hour
},
],
},
{ event: "deployment/requested" },
async ({ event, step }) => {
const jobData = event.data as DeployJob;
return await step.run("execute-deployment", async () => {
logger.info("Deploying started");
try {
const result = await deploy(jobData);
logger.info("Deployment finished", result);
// Send success event
await inngest.send({
name: "deployment/completed",
data: {
...jobData,
result,
status: "success",
},
});
return result;
} catch (error) {
logger.error("Deployment failed", { jobData, error });
// Send failure event
await inngest.send({
name: "deployment/failed",
data: {
...jobData,
error: error instanceof Error ? error.message : String(error),
status: "failed",
},
});
throw error;
}
});
},
);
app.use(async (c, next) => {
if (c.req.path === "/health" || c.req.path === "/api/inngest") {
return next();
}
const authHeader = c.req.header("X-API-Key");
if (process.env.API_KEY !== authHeader) {
return c.json({ message: "Invalid API Key" }, 403);
}
return next();
});
app.post("/deploy", zValidator("json", deployJobSchema), async (c) => {
const data = c.req.valid("json");
logger.info("Received deployment request", data);
try {
// Send event to Inngest instead of adding to Redis queue
await inngest.send({
name: "deployment/requested",
data,
});
logger.info("Deployment event sent to Inngest", {
serverId: data.serverId,
});
return c.json(
{
message: "Deployment Added to Inngest Queue",
serverId: data.serverId,
},
200,
);
} catch (error) {
logger.error("Failed to send deployment event", error);
return c.json(
{
message: "Failed to queue deployment",
error: error instanceof Error ? error.message : String(error),
},
500,
);
}
});
app.post(
"/cancel-deployment",
zValidator("json", cancelDeploymentSchema),
async (c) => {
const data = c.req.valid("json");
logger.info("Received cancel deployment request", data);
try {
// Send cancellation event to Inngest
await inngest.send({
name: "deployment/cancelled",
data,
});
const identifier =
data.applicationType === "application"
? `applicationId: ${data.applicationId}`
: `composeId: ${data.composeId}`;
logger.info("Deployment cancellation event sent", {
...data,
identifier,
});
return c.json({
message: "Deployment cancellation requested",
applicationType: data.applicationType,
});
} catch (error) {
logger.error("Failed to send deployment cancellation event", error);
return c.json(
{
message: "Failed to cancel deployment",
error: error instanceof Error ? error.message : String(error),
},
500,
);
}
},
);
app.get("/health", async (c) => {
return c.json({ status: "ok" });
});
// List deployment jobs (Inngest runs) for a server - same shape as BullMQ queue for the UI
app.get("/jobs", async (c) => {
const serverId = c.req.query("serverId");
if (!serverId) {
return c.json({ message: "serverId is required" }, 400);
}
try {
const rows = await fetchDeploymentJobs(serverId);
return c.json(rows);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (message.includes("INNGEST_BASE_URL")) {
return c.json(
{ message: "INNGEST_BASE_URL is required to list deployment jobs" },
503,
);
}
logger.error("Failed to fetch jobs from Inngest", { serverId, error });
return c.json([], 200);
}
});
// Serve Inngest functions endpoint
app.on(
["GET", "POST", "PUT"],
"/api/inngest",
serveInngest({
client: inngest,
functions: [deploymentFunction],
}),
);
const port = Number.parseInt(process.env.PORT || "3000");
logger.info("Starting Deployments Server with Inngest ✅", port);
serve({ fetch: app.fetch, port });
================================================
FILE: apps/api/src/logger.ts
================================================
import pino from "pino";
export const logger = pino({
transport: {
target: "pino-pretty",
options: {
colorize: true,
},
},
});
================================================
FILE: apps/api/src/schema.ts
================================================
import { z } from "zod";
export const deployJobSchema = z.discriminatedUnion("applicationType", [
z.object({
applicationId: z.string(),
titleLog: z.string().optional(),
descriptionLog: z.string().optional(),
server: z.boolean().optional(),
type: z.enum(["deploy", "redeploy"]),
applicationType: z.literal("application"),
serverId: z.string().min(1),
}),
z.object({
composeId: z.string(),
titleLog: z.string().optional(),
descriptionLog: z.string().optional(),
server: z.boolean().optional(),
type: z.enum(["deploy", "redeploy"]),
applicationType: z.literal("compose"),
serverId: z.string().min(1),
}),
z.object({
applicationId: z.string(),
previewDeploymentId: z.string(),
titleLog: z.string().optional(),
descriptionLog: z.string().optional(),
server: z.boolean().optional(),
type: z.enum(["deploy", "redeploy"]),
applicationType: z.literal("application-preview"),
serverId: z.string().min(1),
}),
]);
export type DeployJob = z.infer<typeof deployJobSchema>;
export const cancelDeploymentSchema = z.discriminatedUnion("applicationType", [
z.object({
applicationId: z.string(),
applicationType: z.literal("application"),
}),
z.object({
composeId: z.string(),
applicationType: z.literal("compose"),
}),
]);
export type CancelDeploymentJob = z.infer<typeof cancelDeploymentSchema>;
================================================
FILE: apps/api/src/service.ts
================================================
import { logger } from "./logger.js";
const baseUrl = process.env.INNGEST_BASE_URL ?? "";
const signingKey = process.env.INNGEST_SIGNING_KEY ?? "";
const DEFAULT_MAX_EVENTS = 500;
const MAX_EVENTS = DEFAULT_MAX_EVENTS;
/** Event shape from GET /v1/events (https://api.inngest.com/v1/events) */
type InngestEventRow = {
internal_id?: string;
accountID?: string;
environmentID?: string;
source?: string;
sourceID?: string | null;
/** RFC3339 timestamp – API uses receivedAt, dev server may use received_at */
receivedAt?: string;
received_at?: string;
id: string;
name: string;
data: Record<string, unknown>;
user?: unknown;
ts: number;
v?: string | null;
metadata?: {
fetchedAt: string;
cachedUntil: string | null;
};
};
/** Run shape from GET /v1/events/{eventId}/runs – the actual job execution */
type InngestRun = {
run_id: string;
event_id: string;
status: string; // "Running" | "Completed" | "Failed" | "Cancelled" | "Queued"?
run_started_at?: string;
ended_at?: string | null;
output?: unknown;
// dev server / API may use different casing
run_started_at_ms?: number;
};
function getEventReceivedAt(ev: InngestEventRow): string | undefined {
return ev.receivedAt ?? ev.received_at;
}
/** Map Inngest run status to BullMQ-style state for the UI */
function runStatusToState(
status: string,
): "pending" | "active" | "completed" | "failed" | "cancelled" {
const s = status.toLowerCase();
if (s === "running") return "active";
if (s === "completed") return "completed";
if (s === "failed") return "failed";
if (s === "cancelled") return "cancelled";
if (s === "queued") return "pending";
return "pending";
}
export const fetchInngestEvents = async () => {
const maxEvents = MAX_EVENTS;
const all: InngestEventRow[] = [];
let cursor: string | undefined;
do {
const params = new URLSearchParams({ limit: "100" });
if (cursor) {
params.set("cursor", cursor);
}
const res = await fetch(`${baseUrl}/v1/events?${params}`, {
headers: {
Authorization: `Bearer ${signingKey}`,
"Content-Type": "application/json",
},
});
if (!res.ok) {
logger.warn("Inngest API error", {
status: res.status,
body: await res.text(),
});
break;
}
const body = (await res.json()) as {
data?: InngestEventRow[];
cursor?: string;
nextCursor?: string;
};
const data = Array.isArray(body.data) ? body.data : [];
all.push(...data);
// Next page: API may return cursor/nextCursor, or use last event's internal_id (per API docs)
const nextCursor =
body.cursor ?? body.nextCursor ?? data[data.length - 1]?.internal_id;
const hasMore = data.length === 100 && nextCursor && all.length < maxEvents;
cursor = hasMore ? nextCursor : undefined;
} while (cursor);
return all.slice(0, maxEvents);
};
/** Fetch runs for a single event (GET /v1/events/{eventId}/runs) – runs are the actual jobs */
export const fetchInngestRunsForEvent = async (
eventId: string,
): Promise<InngestRun[]> => {
const res = await fetch(
`${baseUrl}/v1/events/${encodeURIComponent(eventId)}/runs`,
{
headers: {
Authorization: `Bearer ${signingKey}`,
"Content-Type": "application/json",
},
},
);
if (!res.ok) {
logger.warn("Inngest runs API error", {
eventId,
status: res.status,
body: await res.text(),
});
return [];
}
const body = (await res.json()) as { data?: InngestRun[] };
return Array.isArray(body.data) ? body.data : [];
};
/** One row for the queue UI (BullMQ-compatible shape) */
export type DeploymentJobRow = {
id: string;
name: string;
data: Record<string, unknown>;
timestamp: number;
processedOn?: number;
finishedOn?: number;
failedReason?: string;
state: string;
};
/** Build queue rows from events + their runs (one row per run, or pending if no run yet) */
function buildDeploymentRowsFromRuns(
events: InngestEventRow[],
runsByEventId: Map<string, InngestRun[]>,
serverId: string,
): DeploymentJobRow[] {
const requested = events.filter(
(e) =>
e.name === "deployment/requested" &&
(e.data as Record<string, unknown>)?.serverId === serverId,
);
const rows: DeploymentJobRow[] = [];
for (const ev of requested) {
const data = (ev.data ?? {}) as Record<string, unknown>;
const runs = runsByEventId.get(ev.id) ?? [];
if (runs.length === 0) {
// Queued: event received but no run yet
rows.push({
id: ev.id,
name: ev.name,
data,
timestamp: ev.ts,
processedOn: ev.ts,
finishedOn: undefined,
failedReason: undefined,
state: "pending",
});
continue;
}
for (const run of runs) {
const state = runStatusToState(run.status);
const runStartedMs =
run.run_started_at_ms ??
(run.run_started_at ? new Date(run.run_started_at).getTime() : ev.ts);
const endedMs = run.ended_at
? new Date(run.ended_at).getTime()
: undefined;
const failedReason =
state === "failed" &&
run.output &&
typeof run.output === "object" &&
"error" in run.output
? String((run.output as { error?: unknown }).error)
: undefined;
rows.push({
id: run.run_id,
name: ev.name,
data,
timestamp: runStartedMs,
processedOn: runStartedMs,
finishedOn:
state === "completed" || state === "failed" || state === "cancelled"
? endedMs
: undefined,
failedReason,
state,
});
}
}
return rows.sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
}
/** Fetch deployment jobs for a server: events → runs → rows (correct model: runs = jobs) */
export const fetchDeploymentJobs = async (
serverId: string,
): Promise<DeploymentJobRow[]> => {
if (!signingKey) {
logger.warn("INNGEST_SIGNING_KEY not set, returning empty jobs list");
return [];
}
if (!baseUrl) {
throw new Error("INNGEST_BASE_URL is required to list deployment jobs");
}
const events = await fetchInngestEvents();
const requestedForServer = events.filter(
(e) =>
e.name === "deployment/requested" &&
(e.data as Record<string, unknown>)?.serverId === serverId,
);
// Limit to avoid too many run fetches
const toFetch = requestedForServer.slice(0, 50);
const runsByEventId = new Map<string, InngestRun[]>();
await Promise.all(
toFetch.map(async (ev) => {
const runs = await fetchInngestRunsForEvent(ev.id);
runsByEventId.set(ev.id, runs);
}),
);
return buildDeploymentRowsFromRuns(toFetch, runsByEventId, serverId);
};
================================================
FILE: apps/api/src/utils.ts
================================================
import {
deployApplication,
deployCompose,
deployPreviewApplication,
rebuildApplication,
rebuildCompose,
rebuildPreviewApplication,
updateApplicationStatus,
updateCompose,
updatePreviewDeployment,
} from "@dokploy/server";
import type { DeployJob } from "./schema.js";
export const deploy = async (job: DeployJob) => {
try {
if (job.applicationType === "application") {
await updateApplicationStatus(job.applicationId, "running");
if (job.server) {
if (job.type === "redeploy") {
await rebuildApplication({
applicationId: job.applicationId,
titleLog: job.titleLog || "Rebuild deployment",
descriptionLog: job.descriptionLog || "",
});
} else if (job.type === "deploy") {
await deployApplication({
applicationId: job.applicationId,
titleLog: job.titleLog || "Manual deployment",
descriptionLog: job.descriptionLog || "",
});
}
}
} else if (job.applicationType === "compose") {
await updateCompose(job.composeId, {
composeStatus: "running",
});
if (job.server) {
if (job.type === "redeploy") {
await rebuildCompose({
composeId: job.composeId,
titleLog: job.titleLog || "Rebuild deployment",
descriptionLog: job.descriptionLog || "",
});
} else if (job.type === "deploy") {
await deployCompose({
composeId: job.composeId,
titleLog: job.titleLog || "Manual deployment",
descriptionLog: job.descriptionLog || "",
});
}
}
} else if (job.applicationType === "application-preview") {
await updatePreviewDeployment(job.previewDeploymentId, {
previewStatus: "running",
});
if (job.server) {
if (job.type === "redeploy") {
await rebuildPreviewApplication({
applicationId: job.applicationId,
titleLog: job.titleLog || "Rebuild Preview Deployment",
descriptionLog: job.descriptionLog || "",
previewDeploymentId: job.previewDeploymentId,
});
} else if (job.type === "deploy") {
await deployPreviewApplication({
applicationId: job.applicationId,
titleLog: job.titleLog || "Preview Deployment",
descriptionLog: job.descriptionLog || "",
previewDeploymentId: job.previewDeploymentId,
});
}
}
}
} catch (e) {
if (job.applicationType === "application") {
await updateApplicationStatus(job.applicationId, "error");
} else if (job.applicationType === "compose") {
await updateCompose(job.composeId, {
composeStatus: "error",
});
} else if (job.applicationType === "application-preview") {
await updatePreviewDeployment(job.previewDeploymentId, {
previewStatus: "error",
});
}
throw e;
}
return true;
};
================================================
FILE: apps/api/tsconfig.json
================================================
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"skipLibCheck": true,
"outDir": "dist",
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx",
"baseUrl": ".",
"paths": {
"@/*": ["./*"],
"@dokploy/server/*": ["../../packages/server/src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
================================================
FILE: apps/dokploy/.dockerignore
================================================
node_modules
.git
.gitignore
*.md
dist
================================================
FILE: apps/dokploy/.gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
/redis-data
traefik.yml
.docker
.env.production
# testing
/coverage
/dist
/production-server
# database
/prisma/db.sqlite
/prisma/db.sqlite-journal
/logs
# next.js
/.next/
/out/
next-env.d.ts
/dokploy
/config
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
.env
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
# otros
/.data
/.main
.vscode
*.lockb
*.rdb
.idea
================================================
FILE: apps/dokploy/__test__/cluster/upload.test.ts
================================================
import type { Registry } from "@dokploy/server";
import { getRegistryTag } from "@dokploy/server";
import { describe, expect, it } from "vitest";
describe("getRegistryTag", () => {
// Helper to create a mock registry
const createMockRegistry = (overrides: Partial<Registry> = {}): Registry => {
return {
registryId: "test-registry-id",
registryName: "Test Registry",
username: "myuser",
password: "test-password",
registryUrl: "docker.io",
registryType: "cloud",
imagePrefix: null,
createdAt: new Date().toISOString(),
organizationId: "test-org-id",
...overrides,
};
};
describe("with username (no imagePrefix)", () => {
it("should handle simple image name without tag", () => {
const registry = createMockRegistry({ username: "myuser" });
const result = getRegistryTag(registry, "nginx");
expect(result).toBe("docker.io/myuser/nginx");
});
it("should handle image name with tag", () => {
const registry = createMockRegistry({ username: "myuser" });
const result = getRegistryTag(registry, "nginx:latest");
expect(result).toBe("docker.io/myuser/nginx:latest");
});
it("should handle image name with username already present (no duplication)", () => {
const registry = createMockRegistry({ username: "myuser" });
const result = getRegistryTag(registry, "myuser/myprivaterepo");
// Should not duplicate username
expect(result).toBe("docker.io/myuser/myprivaterepo");
});
it("should handle image name with username and tag already present", () => {
const registry = createMockRegistry({ username: "myuser" });
const result = getRegistryTag(registry, "myuser/myprivaterepo:latest");
// Should not duplicate username
expect(result).toBe("docker.io/myuser/myprivaterepo:latest");
});
it("should handle complex image name with username", () => {
const registry = createMockRegistry({ username: "siumauricio" });
const result = getRegistryTag(
registry,
"siumauricio/app-parse-multi-byte-port-e32uh7",
);
// Should not duplicate username
expect(result).toBe(
"docker.io/siumauricio/app-parse-multi-byte-port-e32uh7",
);
});
it("should handle image name with different username (should not duplicate)", () => {
const registry = createMockRegistry({ username: "myuser" });
const result = getRegistryTag(registry, "otheruser/myprivaterepo");
expect(result).toBe("docker.io/myuser/myprivaterepo");
});
it("should handle image name with full registry URL (no username)", () => {
const registry = createMockRegistry({ username: "myuser" });
const result = getRegistryTag(registry, "docker.io/nginx");
// Should add username since imageName doesn't have one
expect(result).toBe("docker.io/myuser/nginx");
});
it("should handle image name with custom registry URL and username", () => {
const registry = createMockRegistry({ username: "myuser" });
const result = getRegistryTag(registry, "ghcr.io/myuser/repo");
// Should not duplicate username even if registry URL is different
expect(result).toBe("docker.io/myuser/repo");
});
it("should handle image name with custom registry URL (different username)", () => {
const registry = createMockRegistry({ username: "myuser" });
const result = getRegistryTag(registry, "ghcr.io/otheruser/repo");
// Should use registry username, not the one in imageName
expect(result).toBe("docker.io/myuser/repo");
});
});
describe("with imagePrefix", () => {
it("should use imagePrefix instead of username", () => {
const registry = createMockRegistry({
username: "myuser",
imagePrefix: "myorg",
});
const result = getRegistryTag(registry, "nginx");
expect(result).toBe("docker.io/myorg/nginx");
});
it("should use imagePrefix with image tag", () => {
const registry = createMockRegistry({
username: "myuser",
imagePrefix: "myorg",
});
const result = getRegistryTag(registry, "nginx:latest");
expect(result).toBe("docker.io/myorg/nginx:latest");
});
it("should handle imagePrefix with username already in image name", () => {
const registry = createMockRegistry({
username: "myuser",
imagePrefix: "myorg",
});
const result = getRegistryTag(registry, "myuser/myprivaterepo");
expect(result).toBe("docker.io/myorg/myprivaterepo");
});
it("should handle imagePrefix matching image name prefix", () => {
const registry = createMockRegistry({
username: "myuser",
imagePrefix: "myorg",
});
const result = getRegistryTag(registry, "myorg/myprivaterepo");
// Should not duplicate prefix
expect(result).toBe("docker.io/myorg/myprivaterepo");
});
});
describe("without registryUrl", () => {
it("should work without registryUrl", () => {
const registry = createMockRegistry({
username: "myuser",
registryUrl: "",
});
const result = getRegistryTag(registry, "nginx");
expect(result).toBe("myuser/nginx");
});
it("should work without registryUrl with imagePrefix", () => {
const registry = createMockRegistry({
username: "myuser",
imagePrefix: "myorg",
registryUrl: "",
});
const result = getRegistryTag(registry, "nginx");
expect(result).toBe("myorg/nginx");
});
it("should handle username already present without registryUrl", () => {
const registry = createMockRegistry({
username: "myuser",
registryUrl: "",
});
const result = getRegistryTag(registry, "myuser/myprivaterepo");
// Should not duplicate username
expect(result).toBe("myuser/myprivaterepo");
});
});
describe("with custom registryUrl", () => {
it("should handle custom registry URL", () => {
const registry = createMockRegistry({
username: "myuser",
registryUrl: "ghcr.io",
});
const result = getRegistryTag(registry, "nginx");
expect(result).toBe("ghcr.io/myuser/nginx");
});
it("should handle custom registry URL with imagePrefix", () => {
const registry = createMockRegistry({
username: "myuser",
imagePrefix: "myorg",
registryUrl: "ghcr.io",
});
const result = getRegistryTag(registry, "nginx");
expect(result).toBe("ghcr.io/myorg/nginx");
});
it("should handle custom registry URL with username already present", () => {
const registry = createMockRegistry({
username: "myuser",
registryUrl: "ghcr.io",
});
const result = getRegistryTag(registry, "myuser/myprivaterepo");
// Should not duplicate username
expect(result).toBe("ghcr.io/myuser/myprivaterepo");
});
});
describe("edge cases", () => {
it("should handle empty image name", () => {
const registry = createMockRegistry({ username: "myuser" });
const result = getRegistryTag(registry, "");
expect(result).toBe("docker.io/myuser/");
});
it("should handle image name with multiple slashes", () => {
const registry = createMockRegistry({ username: "myuser" });
const result = getRegistryTag(registry, "org/suborg/repo");
expect(result).toBe("docker.io/myuser/repo");
});
it("should handle image name with username at different position", () => {
const registry = createMockRegistry({ username: "myuser" });
const result = getRegistryTag(registry, "org/myuser/repo");
expect(result).toBe("docker.io/myuser/repo");
});
});
describe("special characters in username", () => {
it("should handle Harbor robot account username with $ (e.g. robot$library+dokploy)", () => {
const registry = createMockRegistry({
username: "robot$library+dokploy",
});
const result = getRegistryTag(registry, "nginx");
expect(result).toBe("docker.io/robot$library+dokploy/nginx");
});
it("should handle username with $ and other special characters", () => {
const registry = createMockRegistry({
username: "robot$test+app",
});
const result = getRegistryTag(registry, "myapp:latest");
expect(result).toBe("docker.io/robot$test+app/myapp:latest");
});
it("should handle username with multiple $ symbols", () => {
const registry = createMockRegistry({
username: "user$name$test",
});
const result = getRegistryTag(registry, "app");
expect(result).toBe("docker.io/user$name$test/app");
});
it("should handle username with + and - symbols", () => {
const registry = createMockRegistry({
username: "robot+test-user",
});
const result = getRegistryTag(registry, "nginx:latest");
expect(result).toBe("docker.io/robot+test-user/nginx:latest");
});
});
});
================================================
FILE: apps/dokploy/__test__/compose/compose.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import { addSuffixToAllProperties } from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
const composeFile1 = `
version: "3.8"
services:
web:
image: nginx:latest
container_name: web_container
depends_on:
- app
networks:
- frontend
volumes_from:
- data
links:
- db
extends:
service: base_service
configs:
- source: web_config
app:
image: node:14
networks:
- backend
- frontend
db:
image: postgres:13
networks:
- backend
data:
image: busybox
volumes:
- /data
base_service:
image: base:latest
networks:
frontend:
driver: bridge
backend:
driver: bridge
volumes:
web_data:
driver: local
configs:
web_config:
file: ./web_config.yml
secrets:
db_password:
file: ./db_password.txt
`;
const expectedComposeFile1 = parse(`
version: "3.8"
services:
web-testhash:
image: nginx:latest
container_name: web_container-testhash
depends_on:
- app-testhash
networks:
- frontend-testhash
volumes_from:
- data-testhash
links:
- db-testhash
extends:
service: base_service-testhash
configs:
- source: web_config-testhash
app-testhash:
image: node:14
networks:
- backend-testhash
- frontend-testhash
db-testhash:
image: postgres:13
networks:
- backend-testhash
data-testhash:
image: busybox
volumes:
- /data
base_service-testhash:
image: base:latest
networks:
frontend-testhash:
driver: bridge
backend-testhash:
driver: bridge
volumes:
web_data-testhash:
driver: local
configs:
web_config-testhash:
file: ./web_config.yml
secrets:
db_password-testhash:
file: ./db_password.txt
`) as ComposeSpecification;
test("Add suffix to all properties in compose file 1", () => {
const composeData = parse(composeFile1) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllProperties(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFile1);
});
const composeFile2 = `
version: "3.8"
services:
frontend:
image: nginx:latest
depends_on:
- backend
networks:
- public
volumes_from:
- logs
links:
- cache
extends:
service: shared_service
secrets:
- db_password
backend:
image: node:14
networks:
- private
- public
cache:
image: redis:latest
networks:
- private
logs:
image: busybox
volumes:
- /logs
shared_service:
image: shared:latest
networks:
public:
driver: bridge
private:
driver: bridge
volumes:
logs:
driver: local
configs:
app_config:
file: ./app_config.yml
secrets:
db_password:
file: ./db_password.txt
`;
const expectedComposeFile2 = parse(`
version: "3.8"
services:
frontend-testhash:
image: nginx:latest
depends_on:
- backend-testhash
networks:
- public-testhash
volumes_from:
- logs-testhash
links:
- cache-testhash
extends:
service: shared_service-testhash
secrets:
- db_password-testhash
backend-testhash:
image: node:14
networks:
- private-testhash
- public-testhash
cache-testhash:
image: redis:latest
networks:
- private-testhash
logs-testhash:
image: busybox
volumes:
- /logs
shared_service-testhash:
image: shared:latest
networks:
public-testhash:
driver: bridge
private-testhash:
driver: bridge
volumes:
logs-testhash:
driver: local
configs:
app_config-testhash:
file: ./app_config.yml
secrets:
db_password-testhash:
file: ./db_password.txt
`) as ComposeSpecification;
test("Add suffix to all properties in compose file 2", () => {
const composeData = parse(composeFile2) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllProperties(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFile2);
});
const composeFile3 = `
version: "3.8"
services:
service_a:
image: service_a:latest
depends_on:
- service_b
networks:
- net_a
volumes_from:
- data_volume
links:
- service_c
extends:
service: common_service
configs:
- source: service_a_config
service_b:
image: service_b:latest
networks:
- net_b
- net_a
service_c:
image: service_c:latest
networks:
- net_b
data_volume:
image: busybox
volumes:
- /data
common_service:
image: common:latest
networks:
net_a:
driver: bridge
net_b:
driver: bridge
volumes:
data_volume:
driver: local
configs:
service_a_config:
file: ./service_a_config.yml
secrets:
service_secret:
file: ./service_secret.txt
`;
const expectedComposeFile3 = parse(`
version: "3.8"
services:
service_a-testhash:
image: service_a:latest
depends_on:
- service_b-testhash
networks:
- net_a-testhash
volumes_from:
- data_volume-testhash
links:
- service_c-testhash
extends:
service: common_service-testhash
configs:
- source: service_a_config-testhash
service_b-testhash:
image: service_b:latest
networks:
- net_b-testhash
- net_a-testhash
service_c-testhash:
image: service_c:latest
networks:
- net_b-testhash
data_volume-testhash:
image: busybox
volumes:
- /data
common_service-testhash:
image: common:latest
networks:
net_a-testhash:
driver: bridge
net_b-testhash:
driver: bridge
volumes:
data_volume-testhash:
driver: local
configs:
service_a_config-testhash:
file: ./service_a_config.yml
secrets:
service_secret-testhash:
file: ./service_secret.txt
`) as ComposeSpecification;
test("Add suffix to all properties in compose file 3", () => {
const composeData = parse(composeFile3) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllProperties(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFile3);
});
const composeFile = `
version: "3.8"
services:
plausible_db:
image: postgres:16-alpine
restart: always
volumes:
- db-data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=postgres
plausible_events_db:
image: clickhouse/clickhouse-server:24.3.3.102-alpine
restart: always
volumes:
- event-data:/var/lib/clickhouse
- event-logs:/var/log/clickhouse-server
- ./clickhouse/clickhouse-config.xml:/etc/clickhouse-server/config.d/logging.xml:ro
- ./clickhouse/clickhouse-user-config.xml:/etc/clickhouse-server/users.d/logging.xml:ro
ulimits:
nofile:
soft: 262144
hard: 262144
plausible:
image: ghcr.io/plausible/community-edition:v2.1.0
restart: always
command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
depends_on:
- plausible_db
- plausible_events_db
ports:
- 127.0.0.1:8000:8000
env_file:
- plausible-conf.env
volumes:
db-data:
driver: local
event-data:
driver: local
event-logs:
driver: local
`;
const expectedComposeFile = parse(`
version: "3.8"
services:
plausible_db-testhash:
image: postgres:16-alpine
restart: always
volumes:
- db-data-testhash:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=postgres
plausible_events_db-testhash:
image: clickhouse/clickhouse-server:24.3.3.102-alpine
restart: always
volumes:
- event-data-testhash:/var/lib/clickhouse
- event-logs-testhash:/var/log/clickhouse-server
- ./clickhouse/clickhouse-config.xml:/etc/clickhouse-server/config.d/logging.xml:ro
- ./clickhouse/clickhouse-user-config.xml:/etc/clickhouse-server/users.d/logging.xml:ro
ulimits:
nofile:
soft: 262144
hard: 262144
plausible-testhash:
image: ghcr.io/plausible/community-edition:v2.1.0
restart: always
command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
depends_on:
- plausible_db-testhash
- plausible_events_db-testhash
ports:
- 127.0.0.1:8000:8000
env_file:
- plausible-conf.env
volumes:
db-data-testhash:
driver: local
event-data-testhash:
driver: local
event-logs-testhash:
driver: local
`) as ComposeSpecification;
test("Add suffix to all properties in Plausible compose file", () => {
const composeData = parse(composeFile) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllProperties(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFile);
});
================================================
FILE: apps/dokploy/__test__/compose/config/config-root.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import { addSuffixToConfigsRoot, generateRandomHash } from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
test("Generate random hash with 8 characters", () => {
const hash = generateRandomHash();
expect(hash).toBeDefined();
expect(hash.length).toBe(8);
});
const composeFile = `
version: "3.8"
services:
web:
image: nginx:latest
configs:
web-config:
file: ./web-config.yml
`;
test("Add suffix to configs in root property", () => {
const composeData = parse(composeFile) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.configs) {
return;
}
const configs = addSuffixToConfigsRoot(composeData.configs, suffix);
expect(configs).toBeDefined();
for (const configKey of Object.keys(configs)) {
expect(configKey).toContain(`-${suffix}`);
expect(configs[configKey]).toBeDefined();
}
});
const composeFileMultipleConfigs = `
version: "3.8"
services:
web:
image: nginx:latest
configs:
- source: web-config
target: /etc/nginx/nginx.conf
- source: another-config
target: /etc/nginx/another.conf
configs:
web-config:
file: ./web-config.yml
another-config:
file: ./another-config.yml
`;
test("Add suffix to multiple configs in root property", () => {
const composeData = parse(composeFileMultipleConfigs) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.configs) {
return;
}
const configs = addSuffixToConfigsRoot(composeData.configs, suffix);
expect(configs).toBeDefined();
for (const configKey of Object.keys(configs)) {
expect(configKey).toContain(`-${suffix}`);
expect(configs[configKey]).toBeDefined();
}
expect(configs).toHaveProperty(`web-config-${suffix}`);
expect(configs).toHaveProperty(`another-config-${suffix}`);
});
const composeFileDifferentProperties = `
version: "3.8"
services:
web:
image: nginx:latest
configs:
web-config:
file: ./web-config.yml
special-config:
external: true
`;
test("Add suffix to configs with different properties in root property", () => {
const composeData = parse(
composeFileDifferentProperties,
) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.configs) {
return;
}
const configs = addSuffixToConfigsRoot(composeData.configs, suffix);
expect(configs).toBeDefined();
for (const configKey of Object.keys(configs)) {
expect(configKey).toContain(`-${suffix}`);
expect(configs[configKey]).toBeDefined();
}
expect(configs).toHaveProperty(`web-config-${suffix}`);
expect(configs).toHaveProperty(`special-config-${suffix}`);
});
const composeFileConfigRoot = `
version: "3.8"
services:
web:
image: nginx:latest
app:
image: node:latest
db:
image: postgres:latest
configs:
web_config:
file: ./web-config.yml
app_config:
file: ./app-config.json
db_config:
file: ./db-config.yml
`;
// Expected compose file con el prefijo `testhash`
const expectedComposeFileConfigRoot = parse(`
version: "3.8"
services:
web:
image: nginx:latest
app:
image: node:latest
db:
image: postgres:latest
configs:
web_config-testhash:
file: ./web-config.yml
app_config-testhash:
file: ./app-config.json
db_config-testhash:
file: ./db-config.yml
`) as ComposeSpecification;
test("Add suffix to configs in root property", () => {
const composeData = parse(composeFileConfigRoot) as ComposeSpecification;
const suffix = "testhash";
if (!composeData?.configs) {
return;
}
const configs = addSuffixToConfigsRoot(composeData.configs, suffix);
const updatedComposeData = { ...composeData, configs };
// Verificar que el resultado coincide con el archivo esperado
expect(updatedComposeData).toEqual(expectedComposeFileConfigRoot);
});
================================================
FILE: apps/dokploy/__test__/compose/config/config-service.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import {
addSuffixToConfigsInServices,
generateRandomHash,
} from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
const composeFile = `
version: "3.8"
services:
web:
image: nginx:latest
configs:
- source: web-config
target: /etc/nginx/nginx.conf
configs:
web-config:
file: ./web-config.yml
`;
test("Add suffix to configs in services", () => {
const composeData = parse(composeFile) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const services = addSuffixToConfigsInServices(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
expect(actualComposeData.services?.web?.configs).toContainEqual({
source: `web-config-${suffix}`,
target: "/etc/nginx/nginx.conf",
});
});
const composeFileSingleServiceConfig = `
version: "3.8"
services:
web:
image: nginx:latest
configs:
- source: web-config
target: /etc/nginx/nginx.conf
configs:
web-config:
file: ./web-config.yml
`;
test("Add suffix to configs in services with single config", () => {
const composeData = parse(
composeFileSingleServiceConfig,
) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const services = addSuffixToConfigsInServices(composeData.services, suffix);
expect(services).toBeDefined();
for (const serviceKey of Object.keys(services)) {
const serviceConfigs = services?.[serviceKey]?.configs;
if (serviceConfigs) {
for (const config of serviceConfigs) {
if (typeof config === "object") {
expect(config.source).toContain(`-${suffix}`);
}
}
}
}
});
const composeFileMultipleServicesConfigs = `
version: "3.8"
services:
web:
image: nginx:latest
configs:
- source: web-config
target: /etc/nginx/nginx.conf
- source: common-config
target: /etc/nginx/common.conf
app:
image: node:14
configs:
- source: app-config
target: /usr/src/app/config.json
- source: common-config
target: /usr/src/app/common.json
configs:
web-config:
file: ./web-config.yml
app-config:
file: ./app-config.json
common-config:
file: ./common-config.yml
`;
test("Add suffix to configs in services with multiple configs", () => {
const composeData = parse(
composeFileMultipleServicesConfigs,
) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const services = addSuffixToConfigsInServices(composeData.services, suffix);
expect(services).toBeDefined();
for (const serviceKey of Object.keys(services)) {
const serviceConfigs = services?.[serviceKey]?.configs;
if (serviceConfigs) {
for (const config of serviceConfigs) {
if (typeof config === "object") {
expect(config.source).toContain(`-${suffix}`);
}
}
}
}
});
const composeFileConfigServices = `
version: "3.8"
services:
web:
image: nginx:latest
configs:
- source: web_config
target: /etc/nginx/nginx.conf
app:
image: node:latest
configs:
- source: app_config
target: /usr/src/app/config.json
db:
image: postgres:latest
configs:
- source: db_config
target: /etc/postgresql/postgresql.conf
`;
// Expected compose file con el prefijo `testhash`
const expectedComposeFileConfigServices = parse(`
version: "3.8"
services:
web:
image: nginx:latest
configs:
- source: web_config-testhash
target: /etc/nginx/nginx.conf
app:
image: node:latest
configs:
- source: app_config-testhash
target: /usr/src/app/config.json
db:
image: postgres:latest
configs:
- source: db_config-testhash
target: /etc/postgresql/postgresql.conf
`) as ComposeSpecification;
test("Add suffix to configs in services", () => {
const composeData = parse(composeFileConfigServices) as ComposeSpecification;
const suffix = "testhash";
if (!composeData?.services) {
return;
}
const updatedComposeData = addSuffixToConfigsInServices(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
expect(actualComposeData).toEqual(expectedComposeFileConfigServices);
});
================================================
FILE: apps/dokploy/__test__/compose/config/config.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import { addSuffixToAllConfigs, generateRandomHash } from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
test("Generate random hash with 8 characters", () => {
const hash = generateRandomHash();
expect(hash).toBeDefined();
expect(hash.length).toBe(8);
});
const composeFileCombinedConfigs = `
version: "3.8"
services:
web:
image: nginx:latest
configs:
- source: web_config
target: /etc/nginx/nginx.conf
app:
image: node:14
configs:
- source: app_config
target: /usr/src/app/config.json
db:
image: postgres:13
configs:
- source: db_config
target: /etc/postgresql/postgresql.conf
configs:
web_config:
file: ./web-config.yml
app_config:
file: ./app-config.json
db_config:
file: ./db-config.yml
`;
const expectedComposeFileCombinedConfigs = parse(`
version: "3.8"
services:
web:
image: nginx:latest
configs:
- source: web_config-testhash
target: /etc/nginx/nginx.conf
app:
image: node:14
configs:
- source: app_config-testhash
target: /usr/src/app/config.json
db:
image: postgres:13
configs:
- source: db_config-testhash
target: /etc/postgresql/postgresql.conf
configs:
web_config-testhash:
file: ./web-config.yml
app_config-testhash:
file: ./app-config.json
db_config-testhash:
file: ./db-config.yml
`) as ComposeSpecification;
test("Add suffix to all configs in root and services", () => {
const composeData = parse(composeFileCombinedConfigs) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllConfigs(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFileCombinedConfigs);
});
const composeFileWithEnvAndExternal = `
version: "3.8"
services:
web:
image: nginx:latest
configs:
- source: web_config
target: /etc/nginx/nginx.conf
environment:
- NGINX_CONFIG=/etc/nginx/nginx.conf
app:
image: node:14
configs:
- source: app_config
target: /usr/src/app/config.json
db:
image: postgres:13
configs:
- source: db_config
target: /etc/postgresql/postgresql.conf
configs:
web_config:
external: true
app_config:
file: ./app-config.json
db_config:
environment: dev
file: ./db-config.yml
`;
const expectedComposeFileWithEnvAndExternal = parse(`
version: "3.8"
services:
web:
image: nginx:latest
configs:
- source: web_config-testhash
target: /etc/nginx/nginx.conf
environment:
- NGINX_CONFIG=/etc/nginx/nginx.conf
app:
image: node:14
configs:
- source: app_config-testhash
target: /usr/src/app/config.json
db:
image: postgres:13
configs:
- source: db_config-testhash
target: /etc/postgresql/postgresql.conf
configs:
web_config-testhash:
external: true
app_config-testhash:
file: ./app-config.json
db_config-testhash:
environment: dev
file: ./db-config.yml
`) as ComposeSpecification;
test("Add suffix to configs with environment and external", () => {
const composeData = parse(
composeFileWithEnvAndExternal,
) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllConfigs(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFileWithEnvAndExternal);
});
const composeFileWithTemplateDriverAndLabels = `
version: "3.8"
services:
web:
image: nginx:latest
configs:
- source: web_config
target: /etc/nginx/nginx.conf
app:
image: node:14
configs:
- source: app_config
target: /usr/src/app/config.json
configs:
web_config:
file: ./web-config.yml
template_driver: golang
app_config:
file: ./app-config.json
labels:
- app=frontend
db_config:
file: ./db-config.yml
`;
const expectedComposeFileWithTemplateDriverAndLabels = parse(`
version: "3.8"
services:
web:
image: nginx:latest
configs:
- source: web_config-testhash
target: /etc/nginx/nginx.conf
app:
image: node:14
configs:
- source: app_config-testhash
target: /usr/src/app/config.json
configs:
web_config-testhash:
file: ./web-config.yml
template_driver: golang
app_config-testhash:
file: ./app-config.json
labels:
- app=frontend
db_config-testhash:
file: ./db-config.yml
`) as ComposeSpecification;
test("Add suffix to configs with template driver and labels", () => {
const composeData = parse(
composeFileWithTemplateDriverAndLabels,
) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllConfigs(composeData, suffix);
expect(updatedComposeData).toEqual(
expectedComposeFileWithTemplateDriverAndLabels,
);
});
================================================
FILE: apps/dokploy/__test__/compose/domain/host-rule-format.test.ts
================================================
import type { Domain } from "@dokploy/server";
import { createDomainLabels } from "@dokploy/server";
import { describe, expect, it } from "vitest";
import { parse, stringify } from "yaml";
/**
* Regression tests for Traefik Host rule label format.
*
* These tests verify that the Host rule is generated with the correct format:
* - Host(`domain.com`) - with opening and closing parentheses
* - Host(`domain.com`) && PathPrefix(`/path`) - for path-based routing
*
* Issue: https://github.com/Dokploy/dokploy/issues/3161
* The bug caused Host rules to be malformed as Host`domain.com`)
* (missing opening parenthesis) which broke all domain routing.
*/
describe("Host rule format regression tests", () => {
const baseDomain: Domain = {
host: "example.com",
port: 8080,
https: false,
uniqueConfigKey: 1,
customCertResolver: null,
certificateType: "none",
applicationId: "",
composeId: "",
domainType: "compose",
serviceName: "test-app",
domainId: "",
path: "/",
createdAt: "",
previewDeploymentId: "",
internalPath: "/",
stripPath: false,
};
describe("Host rule format validation", () => {
it("should generate Host rule with correct parentheses format", async () => {
const labels = await createDomainLabels("test-app", baseDomain, "web");
const ruleLabel = labels.find((l) => l.includes(".rule="));
expect(ruleLabel).toBeDefined();
// Verify exact format: Host(`domain`)
expect(ruleLabel).toMatch(/Host\(`[^`]+`\)/);
// Ensure opening parenthesis is present after Host
expect(ruleLabel).toContain("Host(`example.com`)");
// Ensure it does NOT have the malformed format
expect(ruleLabel).not.toMatch(/Host`[^`]+`\)/);
});
it("should generate PathPrefix with correct parentheses format", async () => {
const labels = await createDomainLabels(
"test-app",
{ ...baseDomain, path: "/api" },
"web",
);
const ruleLabel = labels.find((l) => l.includes(".rule="));
expect(ruleLabel).toBeDefined();
// Verify PathPrefix format
expect(ruleLabel).toMatch(/PathPrefix\(`[^`]+`\)/);
expect(ruleLabel).toContain("PathPrefix(`/api`)");
// Ensure opening parenthesis is present
expect(ruleLabel).not.toMatch(/PathPrefix`[^`]+`\)/);
});
it("should generate combined Host and PathPrefix with correct format", async () => {
const labels = await createDomainLabels(
"test-app",
{ ...baseDomain, path: "/api/v1" },
"websecure",
);
const ruleLabel = labels.find((l) => l.includes(".rule="));
expect(ruleLabel).toBeDefined();
expect(ruleLabel).toBe(
"traefik.http.routers.test-app-1-websecure.rule=Host(`example.com`) && PathPrefix(`/api/v1`)",
);
});
});
describe("YAML serialization preserves Host rule format", () => {
it("should preserve Host rule format through YAML stringify/parse", async () => {
const labels = await createDomainLabels("test-app", baseDomain, "web");
const ruleLabel = labels.find((l) => l.includes(".rule="));
// Simulate compose file structure
const composeSpec = {
services: {
myapp: {
image: "nginx",
labels: labels,
},
},
};
// Stringify to YAML
const yamlOutput = stringify(composeSpec, { lineWidth: 1000 });
// Parse back
const parsed = parse(yamlOutput) as typeof composeSpec;
const parsedRuleLabel = parsed.services.myapp.labels.find((l: string) =>
l.includes(".rule="),
);
// Verify format is preserved
expect(parsedRuleLabel).toBe(ruleLabel);
expect(parsedRuleLabel).toContain("Host(`example.com`)");
expect(parsedRuleLabel).not.toMatch(/Host`[^`]+`\)/);
});
it("should preserve complex rule format through YAML serialization", async () => {
const labels = await createDomainLabels(
"test-app",
{ ...baseDomain, path: "/api", https: true },
"websecure",
);
const composeSpec = {
services: {
myapp: {
labels: labels,
},
},
};
const yamlOutput = stringify(composeSpec, { lineWidth: 1000 });
const parsed = parse(yamlOutput) as typeof composeSpec;
const parsedRuleLabel = parsed.services.myapp.labels.find((l: string) =>
l.includes(".rule="),
);
expect(parsedRuleLabel).toContain(
"Host(`example.com`) && PathPrefix(`/api`)",
);
});
});
describe("Edge cases for domain names", () => {
const domainCases = [
{ name: "simple domain", host: "example.com" },
{ name: "subdomain", host: "app.example.com" },
{ name: "deep subdomain", host: "api.v1.app.example.com" },
{ name: "numeric domain", host: "123.example.com" },
{ name: "hyphenated domain", host: "my-app.example-host.com" },
{ name: "localhost", host: "localhost" },
{ name: "IP address style", host: "192.168.1.100" },
];
for (const { name, host } of domainCases) {
it(`should generate correct Host rule for ${name}: ${host}`, async () => {
const labels = await createDomainLabels(
"test-app",
{ ...baseDomain, host },
"web",
);
const ruleLabel = labels.find((l) => l.includes(".rule="));
expect(ruleLabel).toBeDefined();
expect(ruleLabel).toContain(`Host(\`${host}\`)`);
// Verify parenthesis is present
expect(ruleLabel).toMatch(
new RegExp(`Host\\(\\\`${host.replace(/\./g, "\\.")}\\\`\\)`),
);
});
}
});
describe("Multiple domains scenario", () => {
it("should generate correct format for both web and websecure entrypoints", async () => {
const webLabels = await createDomainLabels("test-app", baseDomain, "web");
const websecureLabels = await createDomainLabels(
"test-app",
baseDomain,
"websecure",
);
const webRule = webLabels.find((l) => l.includes(".rule="));
const websecureRule = websecureLabels.find((l) => l.includes(".rule="));
// Both should have correct format
expect(webRule).toContain("Host(`example.com`)");
expect(websecureRule).toContain("Host(`example.com`)");
// Neither should have malformed format
expect(webRule).not.toMatch(/Host`[^`]+`\)/);
expect(websecureRule).not.toMatch(/Host`[^`]+`\)/);
});
});
describe("Special characters in paths", () => {
const pathCases = [
{ name: "simple path", path: "/api" },
{ name: "nested path", path: "/api/v1/users" },
{ name: "path with hyphen", path: "/api-v1" },
{ name: "path with underscore", path: "/api_v1" },
];
for (const { name, path } of pathCases) {
it(`should generate correct PathPrefix for ${name}: ${path}`, async () => {
const labels = await createDomainLabels(
"test-app",
{ ...baseDomain, path },
"web",
);
const ruleLabel = labels.find((l) => l.includes(".rule="));
expect(ruleLabel).toBeDefined();
expect(ruleLabel).toContain(`PathPrefix(\`${path}\`)`);
// Verify parenthesis is present
expect(ruleLabel).not.toMatch(/PathPrefix`[^`]+`\)/);
});
}
});
});
================================================
FILE: apps/dokploy/__test__/compose/domain/labels.test.ts
================================================
import type { Domain } from "@dokploy/server";
import { createDomainLabels } from "@dokploy/server";
import { describe, expect, it } from "vitest";
describe("createDomainLabels", () => {
const appName = "test-app";
const baseDomain: Domain = {
host: "example.com",
port: 8080,
https: false,
uniqueConfigKey: 1,
customCertResolver: null,
certificateType: "none",
applicationId: "",
composeId: "",
domainType: "compose",
serviceName: "test-app",
domainId: "",
path: "/",
createdAt: "",
previewDeploymentId: "",
internalPath: "/",
stripPath: false,
};
it("should create basic labels for web entrypoint", async () => {
const labels = await createDomainLabels(appName, baseDomain, "web");
expect(labels).toEqual([
"traefik.http.routers.test-app-1-web.rule=Host(`example.com`)",
"traefik.http.routers.test-app-1-web.entrypoints=web",
"traefik.http.services.test-app-1-web.loadbalancer.server.port=8080",
"traefik.http.routers.test-app-1-web.service=test-app-1-web",
]);
});
it("should create labels for websecure entrypoint", async () => {
const labels = await createDomainLabels(appName, baseDomain, "websecure");
expect(labels).toEqual([
"traefik.http.routers.test-app-1-websecure.rule=Host(`example.com`)",
"traefik.http.routers.test-app-1-websecure.entrypoints=websecure",
"traefik.http.services.test-app-1-websecure.loadbalancer.server.port=8080",
"traefik.http.routers.test-app-1-websecure.service=test-app-1-websecure",
]);
});
it("should add the path prefix if is different than / empty", async () => {
const labels = await createDomainLabels(
appName,
{
...baseDomain,
path: "/hello",
},
"websecure",
);
expect(labels).toEqual([
"traefik.http.routers.test-app-1-websecure.rule=Host(`example.com`) && PathPrefix(`/hello`)",
"traefik.http.routers.test-app-1-websecure.entrypoints=websecure",
"traefik.http.services.test-app-1-websecure.loadbalancer.server.port=8080",
"traefik.http.routers.test-app-1-websecure.service=test-app-1-websecure",
]);
});
it("should add redirect middleware for https on web entrypoint", async () => {
const httpsBaseDomain = { ...baseDomain, https: true };
const labels = await createDomainLabels(appName, httpsBaseDomain, "web");
expect(labels).toContain(
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file",
);
});
it("should add Let's Encrypt configuration for websecure with letsencrypt certificate", async () => {
const letsencryptDomain = {
...baseDomain,
https: true,
certificateType: "letsencrypt" as const,
};
const labels = await createDomainLabels(
appName,
letsencryptDomain,
"websecure",
);
expect(labels).toContain(
"traefik.http.routers.test-app-1-websecure.tls.certresolver=letsencrypt",
);
});
it("should not add Let's Encrypt configuration for non-letsencrypt certificate", async () => {
const nonLetsencryptDomain = {
...baseDomain,
https: true,
certificateType: "none" as const,
};
const labels = await createDomainLabels(
appName,
nonLetsencryptDomain,
"websecure",
);
expect(labels).not.toContain(
"traefik.http.routers.test-app-1-websecure.tls.certresolver=letsencrypt",
);
});
it("should handle different ports correctly", async () => {
const customPortDomain = { ...baseDomain, port: 3000 };
const labels = await createDomainLabels(appName, customPortDomain, "web");
expect(labels).toContain(
"traefik.http.services.test-app-1-web.loadbalancer.server.port=3000",
);
});
it("should add stripPath middleware when stripPath is enabled", async () => {
const stripPathDomain = {
...baseDomain,
path: "/api",
stripPath: true,
};
const labels = await createDomainLabels(appName, stripPathDomain, "web");
expect(labels).toContain(
"traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api",
);
expect(labels).toContain(
"traefik.http.routers.test-app-1-web.middlewares=stripprefix-test-app-1",
);
});
it("should add internalPath middleware when internalPath is set", async () => {
const internalPathDomain = {
...baseDomain,
internalPath: "/hello",
};
const webLabels = await createDomainLabels(
appName,
internalPathDomain,
"web",
);
const websecureLabels = await createDomainLabels(
appName,
internalPathDomain,
"websecure",
);
// Middleware definition should only appear in web entrypoint
expect(webLabels).toContain(
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
);
expect(websecureLabels).not.toContain(
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
);
// Both routers should reference the middleware
expect(webLabels).toContain(
"traefik.http.routers.test-app-1-web.middlewares=addprefix-test-app-1",
);
expect(websecureLabels).toContain(
"traefik.http.routers.test-app-1-websecure.middlewares=addprefix-test-app-1",
);
});
it("should combine HTTPS redirect with internalPath middleware in correct order", async () => {
const combinedDomain = {
...baseDomain,
https: true,
internalPath: "/hello",
};
const webLabels = await createDomainLabels(appName, combinedDomain, "web");
const websecureLabels = await createDomainLabels(
appName,
combinedDomain,
"websecure",
);
// Web entrypoint should have both middlewares with redirect first
expect(webLabels).toContain(
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file,addprefix-test-app-1",
);
// Websecure should only have the addprefix middleware
expect(websecureLabels).toContain(
"traefik.http.routers.test-app-1-websecure.middlewares=addprefix-test-app-1",
);
// Middleware definition should only appear once (in web)
expect(webLabels).toContain(
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
);
expect(websecureLabels).not.toContain(
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
);
});
it("should combine all middlewares in correct order", async () => {
const fullDomain = {
...baseDomain,
https: true,
path: "/api",
stripPath: true,
internalPath: "/hello",
};
const webLabels = await createDomainLabels(appName, fullDomain, "web");
// Should have all middleware definitions (only in web)
expect(webLabels).toContain(
"traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api",
);
expect(webLabels).toContain(
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
);
// Should have middlewares in correct order: redirect, stripprefix, addprefix
expect(webLabels).toContain(
"traefik.http.routers.test-app-1-web.middlewares=redirect-to-https@file,stripprefix-test-app-1,addprefix-test-app-1",
);
});
it("should not add middleware definitions for websecure entrypoint", async () => {
const internalPathDomain = {
...baseDomain,
path: "/api",
stripPath: true,
internalPath: "/hello",
};
const websecureLabels = await createDomainLabels(
appName,
internalPathDomain,
"websecure",
);
// Should not contain any middleware definitions
expect(websecureLabels).not.toContain(
"traefik.http.middlewares.stripprefix-test-app-1.stripprefix.prefixes=/api",
);
expect(websecureLabels).not.toContain(
"traefik.http.middlewares.addprefix-test-app-1.addprefix.prefix=/hello",
);
// But should reference the middlewares
expect(websecureLabels).toContain(
"traefik.http.routers.test-app-1-websecure.middlewares=stripprefix-test-app-1,addprefix-test-app-1",
);
});
});
================================================
FILE: apps/dokploy/__test__/compose/domain/network-root.test.ts
================================================
import { addDokployNetworkToRoot } from "@dokploy/server";
import { describe, expect, it } from "vitest";
describe("addDokployNetworkToRoot", () => {
it("should create network object if networks is undefined", () => {
const result = addDokployNetworkToRoot(undefined);
expect(result).toEqual({ "dokploy-network": { external: true } });
});
it("should add network to an empty object", () => {
const result = addDokployNetworkToRoot({});
expect(result).toEqual({ "dokploy-network": { external: true } });
});
it("should not modify existing network configuration", () => {
const existing = { "dokploy-network": { external: false } };
const result = addDokployNetworkToRoot(existing);
expect(result).toEqual({ "dokploy-network": { external: true } });
});
it("should add network alongside existing networks", () => {
const existing = { "other-network": { external: true } };
const result = addDokployNetworkToRoot(existing);
expect(result).toEqual({
"other-network": { external: true },
"dokploy-network": { external: true },
});
});
});
================================================
FILE: apps/dokploy/__test__/compose/domain/network-service.test.ts
================================================
import { addDokployNetworkToService } from "@dokploy/server";
import { describe, expect, it } from "vitest";
describe("addDokployNetworkToService", () => {
it("should add network to an empty array", () => {
const result = addDokployNetworkToService([]);
expect(result).toEqual(["dokploy-network", "default"]);
});
it("should not add duplicate network to an array", () => {
const result = addDokployNetworkToService(["dokploy-network"]);
expect(result).toEqual(["dokploy-network", "default"]);
});
it("should add network to an existing array with other networks", () => {
const result = addDokployNetworkToService(["other-network"]);
expect(result).toEqual(["other-network", "dokploy-network", "default"]);
});
it("should add network to an object if networks is an object", () => {
const result = addDokployNetworkToService({ "other-network": {} });
expect(result).toEqual({
"other-network": {},
"dokploy-network": {},
default: {},
});
});
it("should not duplicate default network when already present", () => {
const result = addDokployNetworkToService(["default", "dokploy-network"]);
expect(result).toEqual(["default", "dokploy-network"]);
});
});
================================================
FILE: apps/dokploy/__test__/compose/network/network-root.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import { addSuffixToNetworksRoot, generateRandomHash } from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
const composeFile = `
version: "3.8"
services:
web:
image: nginx:latest
networks:
- frontend
networks:
frontend:
driver: bridge
driver_opts:
com.docker.network.driver.mtu: 1200
backend:
driver: bridge
attachable: true
external_network:
external: true
`;
test("Generate random hash with 8 characters", () => {
const hash = generateRandomHash();
expect(hash).toBeDefined();
expect(hash.length).toBe(8);
});
test("Add suffix to networks root property", () => {
const composeData = parse(composeFile) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.networks) {
return;
}
const networks = addSuffixToNetworksRoot(composeData.networks, suffix);
expect(networks).toBeDefined();
for (const volumeKey of Object.keys(networks)) {
expect(volumeKey).toContain(`-${suffix}`);
}
});
const composeFile2 = `
version: "3.8"
services:
app:
image: myapp:latest
networks:
- app_net
networks:
app_net:
driver: bridge
driver_opts:
com.docker.network.driver.mtu: 1500
ipam:
driver: default
config:
- subnet: 172.20.0.0/16
database_net:
driver: overlay
attachable: true
monitoring_net:
driver: bridge
internal: true
`;
test("Add suffix to advanced networks root property (2 TRY)", () => {
const composeData = parse(composeFile2) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.networks) {
return;
}
const networks = addSuffixToNetworksRoot(composeData.networks, suffix);
expect(networks).toBeDefined();
for (const networkKey of Object.keys(networks)) {
expect(networkKey).toContain(`-${suffix}`);
}
});
const composeFile3 = `
version: "3.8"
services:
web:
image: nginx:latest
networks:
- frontend
- backend
networks:
frontend:
external:
name: my_external_network
backend:
driver: bridge
labels:
- "com.example.description=Backend network"
- "com.example.environment=production"
external_network:
external: true
`;
test("Add suffix to networks with external properties", () => {
const composeData = parse(composeFile3) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.networks) {
return;
}
const networks = addSuffixToNetworksRoot(composeData.networks, suffix);
expect(networks).toBeDefined();
for (const networkKey of Object.keys(networks)) {
expect(networkKey).toContain(`-${suffix}`);
}
});
const composeFile4 = `
version: "3.8"
services:
db:
image: postgres:13
networks:
- db_net
networks:
db_net:
driver: bridge
ipam:
config:
- subnet: 192.168.1.0/24
- gateway: 192.168.1.1
- aux_addresses:
host1: 192.168.1.2
host2: 192.168.1.3
external_network:
external: true
`;
test("Add suffix to networks with IPAM configurations", () => {
const composeData = parse(composeFile4) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.networks) {
return;
}
const networks = addSuffixToNetworksRoot(composeData.networks, suffix);
expect(networks).toBeDefined();
for (const networkKey of Object.keys(networks)) {
expect(networkKey).toContain(`-${suffix}`);
}
});
const composeFile5 = `
version: "3.8"
services:
api:
image: myapi:latest
networks:
- api_net
networks:
api_net:
driver: bridge
options:
com.docker.network.bridge.name: br0
enable_ipv6: true
ipam:
driver: default
config:
- subnet: "2001:db8:1::/64"
- gateway: "2001:db8:1::1"
external_network:
external: true
`;
test("Add suffix to networks with custom options", () => {
const composeData = parse(composeFile5) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.networks) {
return;
}
const networks = addSuffixToNetworksRoot(composeData.networks, suffix);
expect(networks).toBeDefined();
for (const networkKey of Object.keys(networks)) {
expect(networkKey).toContain(`-${suffix}`);
}
});
const composeFile6 = `
version: "3.8"
services:
web:
image: nginx:latest
networks:
- frontend
networks:
frontend:
driver: bridge
driver_opts:
com.docker.network.driver.mtu: 1200
backend:
driver: bridge
attachable: true
external_network:
external: true
`;
// Expected compose file with static suffix `testhash`
const expectedComposeFile6 = `
version: "3.8"
services:
web:
image: nginx:latest
networks:
- frontend-testhash
networks:
frontend-testhash:
driver: bridge
driver_opts:
com.docker.network.driver.mtu: 1200
backend-testhash:
driver: bridge
attachable: true
external_network-testhash:
external: true
`;
test("Add suffix to networks with static suffix", () => {
const composeData = parse(composeFile6) as ComposeSpecification;
const suffix = "testhash";
if (!composeData?.networks) {
return;
}
const networks = addSuffixToNetworksRoot(composeData.networks, suffix);
const expectedComposeData = parse(
expectedComposeFile6,
) as ComposeSpecification;
expect(networks).toStrictEqual(expectedComposeData.networks);
});
const composeFile7 = `
version: "3.8"
services:
web:
image: nginx:latest
networks:
- dokploy-network
networks:
dokploy-network:
`;
test("It shoudn't add suffix to dokploy-network", () => {
const composeData = parse(composeFile7) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.networks) {
return;
}
const networks = addSuffixToNetworksRoot(composeData.networks, suffix);
expect(networks).toBeDefined();
for (const networkKey of Object.keys(networks)) {
expect(networkKey).toContain("dokploy-network");
}
});
================================================
FILE: apps/dokploy/__test__/compose/network/network-service.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import {
addSuffixToServiceNetworks,
generateRandomHash,
} from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
const composeFile = `
version: "3.8"
services:
web:
image: nginx:latest
networks:
- frontend
- backend
api:
image: myapi:latest
networks:
- backend
`;
test("Add suffix to networks in services", () => {
const composeData = parse(composeFile) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const services = addSuffixToServiceNetworks(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
expect(actualComposeData?.services?.web?.networks).toContain(
`frontend-${suffix}`,
);
expect(actualComposeData?.services?.api?.networks).toContain(
`backend-${suffix}`,
);
const apiNetworks = actualComposeData?.services?.api?.networks;
expect(apiNetworks).toBeDefined();
expect(actualComposeData?.services?.api?.networks).toContain(
`backend-${suffix}`,
);
});
// Caso 2: Objeto con aliases
const composeFile2 = `
version: "3.8"
services:
api:
image: myapi:latest
networks:
frontend:
aliases:
- api
networks:
frontend:
driver: bridge
`;
test("Add suffix to networks in services with aliases", () => {
const composeData = parse(composeFile2) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const services = addSuffixToServiceNetworks(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
expect(actualComposeData.services?.api?.networks).toHaveProperty(
`frontend-${suffix}`,
);
const networkConfig = actualComposeData?.services?.api?.networks as {
[key: string]: { aliases?: string[] };
};
expect(networkConfig[`frontend-${suffix}`]).toBeDefined();
expect(networkConfig[`frontend-${suffix}`]?.aliases).toContain("api");
expect(actualComposeData.services?.api?.networks).not.toHaveProperty(
"frontend-ash",
);
});
const composeFile3 = `
version: "3.8"
services:
redis:
image: redis:alpine
networks:
backend:
networks:
backend:
driver: bridge
`;
test("Add suffix to networks in services (Object with simple networks)", () => {
const composeData = parse(composeFile3) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const services = addSuffixToServiceNetworks(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
expect(actualComposeData.services?.redis?.networks).toHaveProperty(
`backend-${suffix}`,
);
});
const composeFileCombined = `
version: "3.8"
services:
web:
image: nginx:latest
networks:
- frontend
- backend
api:
image: myapi:latest
networks:
frontend:
aliases:
- api
redis:
image: redis:alpine
networks:
backend:
networks:
frontend:
driver: bridge
backend:
driver: bridge
`;
test("Add suffix to networks in services (combined case)", () => {
const composeData = parse(composeFileCombined) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const services = addSuffixToServiceNetworks(composeData.services, suffix);
const actualComposeData = { ...composeData, services };
// Caso 1: ListOfStrings
expect(actualComposeData.services?.web?.networks).toContain(
`frontend-${suffix}`,
);
expect(actualComposeData.services?.web?.networks).toContain(
`backend-${suffix}`,
);
// Caso 2: Objeto con aliases
const apiNetworks = actualComposeData.services?.api?.networks as {
[key: string]: unknown;
};
expect(apiNetworks).toHaveProperty(`frontend-${suffix}`);
expect(apiNetworks[`frontend-${suffix}`]).toBeDefined();
expect(apiNetworks).not.toHaveProperty("frontend");
// Caso 3: Objeto con redes simples
const redisNetworks = actualComposeData.services?.redis?.networks;
expect(redisNetworks).toHaveProperty(`backend-${suffix}`);
expect(redisNetworks).not.toHaveProperty("backend");
});
const composeFile7 = `
version: "3.8"
services:
web:
image: nginx:latest
networks:
- dokploy-network
`;
test("It shoudn't add suffix to dokploy-network in services", () => {
const composeData = parse(composeFile7) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const networks = addSuffixToServiceNetworks(composeData.services, suffix);
const service = networks.web;
expect(service).toBeDefined();
expect(service?.networks).toContain("dokploy-network");
});
const composeFile8 = `
version: "3.8"
services:
web:
image: nginx:latest
networks:
- frontend
- backend
- dokploy-network
api:
image: myapi:latest
networks:
frontend:
aliases:
- api
dokploy-network:
aliases:
- api
redis:
image: redis:alpine
networks:
dokploy-network:
db:
image: myapi:latest
networks:
dokploy-network:
aliases:
- apid
`;
test("It shoudn't add suffix to dokploy-network in services multiples cases", () => {
const composeData = parse(composeFile8) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.services) {
return;
}
const networks = addSuffixToServiceNetworks(composeData.services, suffix);
const service = networks.web;
const api = networks.api;
const redis = networks.redis;
const db = networks.db;
const dbNetworks = db?.networks as {
[key: string]: unknown;
};
const apiNetworks = api?.networks as {
[key: string]: unknown;
};
expect(service).toBeDefined();
expect(service?.networks).toContain("dokploy-network");
expect(redis?.networks).toHaveProperty("dokploy-network");
expect(dbNetworks["dokploy-network"]).toBeDefined();
expect(apiNetworks["dokploy-network"]).toBeDefined();
});
================================================
FILE: apps/dokploy/__test__/compose/network/network.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import {
addSuffixToAllNetworks,
addSuffixToNetworksRoot,
addSuffixToServiceNetworks,
generateRandomHash,
} from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
const composeFileCombined = `
version: "3.8"
services:
web:
image: nginx:latest
networks:
- frontend
- backend
api:
image: myapi:latest
networks:
frontend:
aliases:
- api
redis:
image: redis:alpine
networks:
backend:
networks:
frontend:
driver: bridge
backend:
driver: bridge
`;
test("Add suffix to networks in services and root (combined case)", () => {
const composeData = parse(composeFileCombined) as ComposeSpecification;
const suffix = generateRandomHash();
// Prefijo para redes definidas en el root
if (composeData.networks) {
composeData.networks = addSuffixToNetworksRoot(
composeData.networks,
suffix,
);
}
// Prefijo para redes definidas en los servicios
if (composeData.services) {
composeData.services = addSuffixToServiceNetworks(
composeData.services,
suffix,
);
}
const actualComposeData = { ...composeData };
// Verificar redes en root
expect(actualComposeData.networks).toHaveProperty(`frontend-${suffix}`);
expect(actualComposeData.networks).toHaveProperty(`backend-${suffix}`);
expect(actualComposeData.networks).not.toHaveProperty("frontend");
expect(actualComposeData.networks).not.toHaveProperty("backend");
// Caso 1: ListOfStrings
expect(actualComposeData.services?.web?.networks).toContain(
`frontend-${suffix}`,
);
expect(actualComposeData.services?.web?.networks).toContain(
`backend-${suffix}`,
);
// Caso 2: Objeto con aliases
const apiNetworks = actualComposeData.services?.api?.networks as {
[key: string]: { aliases?: string[] };
};
expect(apiNetworks).toHaveProperty(`frontend-${suffix}`);
expect(apiNetworks?.[`frontend-${suffix}`]?.aliases).toContain("api");
expect(apiNetworks).not.toHaveProperty("frontend");
// Caso 3: Objeto con redes simples
const redisNetworks = actualComposeData.services?.redis?.networks;
expect(redisNetworks).toHaveProperty(`backend-${suffix}`);
expect(redisNetworks).not.toHaveProperty("backend");
});
const expectedComposeFile = parse(`
version: "3.8"
services:
web:
image: nginx:latest
networks:
- frontend-testhash
- backend-testhash
api:
image: myapi:latest
networks:
frontend-testhash:
aliases:
- api
redis:
image: redis:alpine
networks:
backend-testhash:
networks:
frontend-testhash:
driver: bridge
backend-testhash:
driver: bridge
`);
test("Add suffix to networks in compose file", () => {
const composeData = parse(composeFileCombined) as ComposeSpecification;
const suffix = "testhash";
if (!composeData?.networks) {
return;
}
const updatedComposeData = addSuffixToAllNetworks(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFile);
});
const composeFile2 = `
version: "3.8"
services:
web:
image: nginx:latest
networks:
- frontend
- backend
db:
image: postgres:latest
networks:
backend:
aliases:
- db
networks:
frontend:
external: true
backend:
driver: bridge
`;
const expectedComposeFile2 = parse(`
version: "3.8"
services:
web:
image: nginx:latest
networks:
- frontend-testhash
- backend-testhash
db:
image: postgres:latest
networks:
backend-testhash:
aliases:
- db
networks:
frontend-testhash:
external: true
backend-testhash:
driver: bridge
`);
test("Add suffix to networks in compose file with external and internal networks", () => {
const composeData = parse(composeFile2) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllNetworks(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFile2);
});
const composeFile3 = `
version: "3.8"
services:
app:
image: myapp:latest
networks:
frontend:
aliases:
- app
backend:
worker:
image: worker:latest
networks:
- backend
networks:
frontend:
driver: bridge
attachable: true
backend:
driver: bridge
driver_opts:
com.docker.network.bridge.enable_icc: "true"
`;
const expectedComposeFile3 = parse(`
version: "3.8"
services:
app:
image: myapp:latest
networks:
frontend-testhash:
aliases:
- app
backend-testhash:
worker:
image: worker:latest
networks:
- backend-testhash
networks:
frontend-testhash:
driver: bridge
attachable: true
backend-testhash:
driver: bridge
driver_opts:
com.docker.network.bridge.enable_icc: "true"
`);
test("Add suffix to networks in compose file with multiple services and complex network configurations", () => {
const composeData = parse(composeFile3) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllNetworks(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFile3);
});
const composeFile4 = `
version: "3.8"
services:
app:
image: myapp:latest
networks:
frontend:
aliases:
- app
backend:
dokploy-network:
worker:
image: worker:latest
networks:
- backend
- dokploy-network
networks:
frontend:
driver: bridge
attachable: true
backend:
driver: bridge
driver_opts:
com.docker.network.bridge.enable_icc: "true"
dokploy-network:
driver: bridge
`;
const expectedComposeFile4 = parse(`
version: "3.8"
services:
app:
image: myapp:latest
networks:
frontend-testhash:
aliases:
- app
backend-testhash:
dokploy-network:
worker:
image: worker:latest
networks:
- backend-testhash
- dokploy-network
networks:
frontend-testhash:
driver: bridge
attachable: true
backend-testhash:
driver: bridge
driver_opts:
com.docker.network.bridge.enable_icc: "true"
dokploy-network:
driver: bridge
`);
test("Expect don't add suffix to dokploy-network in compose file with multiple services and complex network configurations", () => {
const composeData = parse(composeFile4) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllNetworks(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFile4);
});
================================================
FILE: apps/dokploy/__test__/compose/secrets/secret-root.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import { addSuffixToSecretsRoot, generateRandomHash } from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
test("Generate random hash with 8 characters", () => {
const hash = generateRandomHash();
expect(hash).toBeDefined();
expect(hash.length).toBe(8);
});
const composeFileSecretsRoot = `
version: "3.8"
services:
web:
image: nginx:latest
secrets:
db_password:
file: ./db_password.txt
`;
test("Add suffix to secrets in root property", () => {
const composeData = parse(composeFileSecretsRoot) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.secrets) {
return;
}
const secrets = addSuffixToSecretsRoot(composeData.secrets, suffix);
expect(secrets).toBeDefined();
if (secrets) {
for (const secretKey of Object.keys(secrets)) {
expect(secretKey).toContain(`-${suffix}`);
expect(secrets[secretKey]).toBeDefined();
}
}
});
const composeFileSecretsRoot1 = `
version: "3.8"
services:
api:
image: myapi:latest
secrets:
api_key:
file: ./api_key.txt
`;
test("Add suffix to secrets in root property (Test 1)", () => {
const composeData = parse(composeFileSecretsRoot1) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.secrets) {
return;
}
const secrets = addSuffixToSecretsRoot(composeData.secrets, suffix);
expect(secrets).toBeDefined();
if (secrets) {
for (const secretKey of Object.keys(secrets)) {
expect(secretKey).toContain(`-${suffix}`);
expect(secrets[secretKey]).toBeDefined();
}
}
});
const composeFileSecretsRoot2 = `
version: "3.8"
services:
frontend:
image: nginx:latest
secrets:
frontend_secret:
file: ./frontend_secret.txt
db_password:
external: true
`;
test("Add suffix to secrets in root property (Test 2)", () => {
const composeData = parse(composeFileSecretsRoot2) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.secrets) {
return;
}
const secrets = addSuffixToSecretsRoot(composeData.secrets, suffix);
expect(secrets).toBeDefined();
if (secrets) {
for (const secretKey of Object.keys(secrets)) {
expect(secretKey).toContain(`-${suffix}`);
expect(secrets[secretKey]).toBeDefined();
}
}
});
================================================
FILE: apps/dokploy/__test__/compose/secrets/secret-services.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import {
addSuffixToSecretsInServices,
generateRandomHash,
} from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
const composeFileSecretsServices = `
version: "3.8"
services:
db:
image: postgres:latest
secrets:
- db_password
secrets:
db_password:
file: ./db_password.txt
`;
test("Add suffix to secrets in services", () => {
const composeData = parse(composeFileSecretsServices) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData.services) {
return;
}
const updatedComposeData = addSuffixToSecretsInServices(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
expect(actualComposeData.services?.db?.secrets).toContain(
`db_password-${suffix}`,
);
});
const composeFileSecretsServices1 = `
version: "3.8"
services:
app:
image: node:14
secrets:
- app_secret
secrets:
app_secret:
file: ./app_secret.txt
`;
test("Add suffix to secrets in services (Test 1)", () => {
const composeData = parse(
composeFileSecretsServices1,
) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData.services) {
return;
}
const updatedComposeData = addSuffixToSecretsInServices(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
expect(actualComposeData.services?.app?.secrets).toContain(
`app_secret-${suffix}`,
);
});
const composeFileSecretsServices2 = `
version: "3.8"
services:
backend:
image: backend:latest
secrets:
- backend_secret
frontend:
image: frontend:latest
secrets:
- frontend_secret
secrets:
backend_secret:
file: ./backend_secret.txt
frontend_secret:
file: ./frontend_secret.txt
`;
test("Add suffix to secrets in services (Test 2)", () => {
const composeData = parse(
composeFileSecretsServices2,
) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData.services) {
return;
}
const updatedComposeData = addSuffixToSecretsInServices(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
expect(actualComposeData.services?.backend?.secrets).toContain(
`backend_secret-${suffix}`,
);
expect(actualComposeData.services?.frontend?.secrets).toContain(
`frontend_secret-${suffix}`,
);
});
================================================
FILE: apps/dokploy/__test__/compose/secrets/secret.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import { addSuffixToAllSecrets } from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
const composeFileCombinedSecrets = `
version: "3.8"
services:
web:
image: nginx:latest
secrets:
- web_secret
app:
image: node:14
secrets:
- app_secret
secrets:
web_secret:
file: ./web_secret.txt
app_secret:
file: ./app_secret.txt
`;
const expectedComposeFileCombinedSecrets = parse(`
version: "3.8"
services:
web:
image: nginx:latest
secrets:
- web_secret-testhash
app:
image: node:14
secrets:
- app_secret-testhash
secrets:
web_secret-testhash:
file: ./web_secret.txt
app_secret-testhash:
file: ./app_secret.txt
`) as ComposeSpecification;
test("Add suffix to all secrets", () => {
const composeData = parse(composeFileCombinedSecrets) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllSecrets(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFileCombinedSecrets);
});
const composeFileCombinedSecrets3 = `
version: "3.8"
services:
api:
image: myapi:latest
secrets:
- api_key
cache:
image: redis:latest
secrets:
- cache_secret
secrets:
api_key:
file: ./api_key.txt
cache_secret:
file: ./cache_secret.txt
`;
const expectedComposeFileCombinedSecrets3 = parse(`
version: "3.8"
services:
api:
image: myapi:latest
secrets:
- api_key-testhash
cache:
image: redis:latest
secrets:
- cache_secret-testhash
secrets:
api_key-testhash:
file: ./api_key.txt
cache_secret-testhash:
file: ./cache_secret.txt
`) as ComposeSpecification;
test("Add suffix to all secrets (3rd Case)", () => {
const composeData = parse(
composeFileCombinedSecrets3,
) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllSecrets(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFileCombinedSecrets3);
});
const composeFileCombinedSecrets4 = `
version: "3.8"
services:
web:
image: nginx:latest
secrets:
- web_secret
db:
image: postgres:latest
secrets:
- db_password
secrets:
web_secret:
file: ./web_secret.txt
db_password:
file: ./db_password.txt
`;
const expectedComposeFileCombinedSecrets4 = parse(`
version: "3.8"
services:
web:
image: nginx:latest
secrets:
- web_secret-testhash
db:
image: postgres:latest
secrets:
- db_password-testhash
secrets:
web_secret-testhash:
file: ./web_secret.txt
db_password-testhash:
file: ./db_password.txt
`) as ComposeSpecification;
test("Add suffix to all secrets (4th Case)", () => {
const composeData = parse(
composeFileCombinedSecrets4,
) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllSecrets(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFileCombinedSecrets4);
});
================================================
FILE: apps/dokploy/__test__/compose/service/service-container-name.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
const composeFile = `
version: "3.8"
services:
web:
image: nginx:latest
container_name: web_container
api:
image: myapi:latest
networks:
default:
driver: bridge
`;
test("Generate random hash with 8 characters", () => {
const hash = generateRandomHash();
expect(hash).toBeDefined();
expect(hash.length).toBe(8);
});
test("Add suffix to service names with container_name in compose file", () => {
const composeData = parse(composeFile) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData.services) {
return;
}
const updatedComposeData = addSuffixToServiceNames(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
// Verificar que el nombre del contenedor ha cambiado correctamente
expect(actualComposeData.services?.[`web-${suffix}`]?.container_name).toBe(
`web_container-${suffix}`,
);
// Verificar que la nueva clave del servicio tiene el prefijo y la vieja clave no existe
expect(actualComposeData.services).toHaveProperty(`web-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
expect(actualComposeData.services?.[`web-${suffix}`]?.image).toBe(
"nginx:latest",
);
expect(actualComposeData.services?.[`api-${suffix}`]?.image).toBe(
"myapi:latest",
);
});
================================================
FILE: apps/dokploy/__test__/compose/service/service-depends-on.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
test("Generate random hash with 8 characters", () => {
const hash = generateRandomHash();
expect(hash).toBeDefined();
expect(hash.length).toBe(8);
});
const composeFile4 = `
version: "3.8"
services:
web:
image: nginx:latest
depends_on:
- db
- api
api:
image: myapi:latest
db:
image: postgres:latest
networks:
default:
driver: bridge
`;
test("Add suffix to service names with depends_on (array) in compose file", () => {
const composeData = parse(composeFile4) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData.services) {
return;
}
const updatedComposeData = addSuffixToServiceNames(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
// Verificar que la nueva clave del servicio tiene el prefijo y la vieja clave no existe
expect(actualComposeData.services).toHaveProperty(`web-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
expect(actualComposeData.services?.[`web-${suffix}`]?.image).toBe(
"nginx:latest",
);
expect(actualComposeData.services?.[`api-${suffix}`]?.image).toBe(
"myapi:latest",
);
// Verificar que los nombres en depends_on tienen el prefijo
expect(actualComposeData.services?.[`web-${suffix}`]?.depends_on).toContain(
`db-${suffix}`,
);
expect(actualComposeData.services?.[`web-${suffix}`]?.depends_on).toContain(
`api-${suffix}`,
);
// Verificar que los servicios `db` y `api` también tienen el prefijo
expect(actualComposeData.services).toHaveProperty(`db-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("db");
expect(actualComposeData.services?.[`db-${suffix}`]?.image).toBe(
"postgres:latest",
);
expect(actualComposeData.services).toHaveProperty(`api-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("api");
expect(actualComposeData.services?.[`api-${suffix}`]?.image).toBe(
"myapi:latest",
);
});
const composeFile5 = `
version: "3.8"
services:
web:
image: nginx:latest
depends_on:
db:
condition: service_healthy
api:
condition: service_started
api:
image: myapi:latest
db:
image: postgres:latest
networks:
default:
driver: bridge
`;
test("Add suffix to service names with depends_on (object) in compose file", () => {
const composeData = parse(composeFile5) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData.services) {
return;
}
const updatedComposeData = addSuffixToServiceNames(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
// Verificar que la nueva clave del servicio tiene el prefijo y la vieja clave no existe
expect(actualComposeData.services).toHaveProperty(`web-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
expect(actualComposeData.services?.[`web-${suffix}`]?.image).toBe(
"nginx:latest",
);
expect(actualComposeData.services?.[`api-${suffix}`]?.image).toBe(
"myapi:latest",
);
// Verificar que los nombres en depends_on tienen el prefijo
const webDependsOn = actualComposeData.services?.[`web-${suffix}`]
?.depends_on as Record<string, any>;
expect(webDependsOn).toHaveProperty(`db-${suffix}`);
expect(webDependsOn).toHaveProperty(`api-${suffix}`);
expect(webDependsOn[`db-${suffix}`].condition).toBe("service_healthy");
expect(webDependsOn[`api-${suffix}`].condition).toBe("service_started");
// Verificar que los servicios `db` y `api` también tienen el prefijo
expect(actualComposeData.services).toHaveProperty(`db-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("db");
expect(actualComposeData.services?.[`db-${suffix}`]?.image).toBe(
"postgres:latest",
);
expect(actualComposeData.services).toHaveProperty(`api-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("api");
expect(actualComposeData.services?.[`api-${suffix}`]?.image).toBe(
"myapi:latest",
);
});
================================================
FILE: apps/dokploy/__test__/compose/service/service-extends.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
test("Generate random hash with 8 characters", () => {
const hash = generateRandomHash();
expect(hash).toBeDefined();
expect(hash.length).toBe(8);
});
const composeFile6 = `
version: "3.8"
services:
web:
image: nginx:latest
extends: base_service
api:
image: myapi:latest
base_service:
image: base:latest
networks:
default:
driver: bridge
`;
test("Add suffix to service names with extends (string) in compose file", () => {
const composeData = parse(composeFile6) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData.services) {
return;
}
const updatedComposeData = addSuffixToServiceNames(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
// Verificar que la nueva clave del servicio tiene el prefijo y la vieja clave no existe
expect(actualComposeData.services).toHaveProperty(`web-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
expect(actualComposeData.services?.[`web-${suffix}`]?.image).toBe(
"nginx:latest",
);
expect(actualComposeData.services?.[`api-${suffix}`]?.image).toBe(
"myapi:latest",
);
// Verificar que el nombre en extends tiene el prefijo
expect(actualComposeData.services?.[`web-${suffix}`]?.extends).toBe(
`base_service-${suffix}`,
);
// Verificar que el servicio `base_service` también tiene el prefijo
expect(actualComposeData.services).toHaveProperty(`base_service-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("base_service");
expect(actualComposeData.services?.[`base_service-${suffix}`]?.image).toBe(
"base:latest",
);
});
const composeFile7 = `
version: "3.8"
services:
web:
image: nginx:latest
extends:
service: base_service
file: docker-compose.base.yml
api:
image: myapi:latest
base_service:
image: base:latest
networks:
default:
driver: bridge
`;
test("Add suffix to service names with extends (object) in compose file", () => {
const composeData = parse(composeFile7) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData.services) {
return;
}
const updatedComposeData = addSuffixToServiceNames(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
// Verificar que la nueva clave del servicio tiene el prefijo y la vieja clave no existe
expect(actualComposeData.services).toHaveProperty(`web-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
expect(actualComposeData.services?.[`web-${suffix}`]?.image).toBe(
"nginx:latest",
);
expect(actualComposeData.services?.[`api-${suffix}`]?.image).toBe(
"myapi:latest",
);
// Verificar que el nombre en extends.service tiene el prefijo
const webExtends = actualComposeData.services?.[`web-${suffix}`]?.extends;
if (typeof webExtends !== "string") {
expect(webExtends?.service).toBe(`base_service-${suffix}`);
}
// Verificar que el servicio `base_service` también tiene el prefijo
expect(actualComposeData.services).toHaveProperty(`base_service-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("base_service");
expect(actualComposeData.services?.[`base_service-${suffix}`]?.image).toBe(
"base:latest",
);
});
================================================
FILE: apps/dokploy/__test__/compose/service/service-links.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
test("Generate random hash with 8 characters", () => {
const hash = generateRandomHash();
expect(hash).toBeDefined();
expect(hash.length).toBe(8);
});
const composeFile2 = `
version: "3.8"
services:
web:
image: nginx:latest
links:
- db
api:
image: myapi:latest
db:
image: postgres:latest
networks:
default:
driver: bridge
`;
test("Add suffix to service names with links in compose file", () => {
const composeData = parse(composeFile2) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData.services) {
return;
}
const updatedComposeData = addSuffixToServiceNames(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
// Verificar que la nueva clave del servicio tiene el prefijo y la vieja clave no existe
expect(actualComposeData.services).toHaveProperty(`web-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
expect(actualComposeData.services?.[`web-${suffix}`]?.image).toBe(
"nginx:latest",
);
expect(actualComposeData.services?.[`api-${suffix}`]?.image).toBe(
"myapi:latest",
);
// Verificar que los nombres en links tienen el prefijo
expect(actualComposeData.services?.[`web-${suffix}`]?.links).toContain(
`db-${suffix}`,
);
// Verificar que los servicios `db` y `api` también tienen el prefijo
expect(actualComposeData.services).toHaveProperty(`db-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("db");
expect(actualComposeData.services?.[`db-${suffix}`]?.image).toBe(
"postgres:latest",
);
expect(actualComposeData.services).toHaveProperty(`api-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("api");
expect(actualComposeData.services?.[`api-${suffix}`]?.image).toBe(
"myapi:latest",
);
});
================================================
FILE: apps/dokploy/__test__/compose/service/service-names.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
test("Generate random hash with 8 characters", () => {
const hash = generateRandomHash();
expect(hash).toBeDefined();
expect(hash.length).toBe(8);
});
const composeFile = `
version: "3.8"
services:
web:
image: nginx:latest
api:
image: myapi:latest
networks:
default:
driver: bridge
`;
test("Add suffix to service names in compose file", () => {
const composeData = parse(composeFile) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData.services) {
return;
}
const updatedComposeData = addSuffixToServiceNames(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
// Verificar que los nombres de los servicios han cambiado correctamente
expect(actualComposeData.services).toHaveProperty(`web-${suffix}`);
expect(actualComposeData.services).toHaveProperty(`api-${suffix}`);
// Verificar que las claves originales no existen
expect(actualComposeData.services).not.toHaveProperty("web");
expect(actualComposeData.services).not.toHaveProperty("api");
});
================================================
FILE: apps/dokploy/__test__/compose/service/service.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import {
addSuffixToAllServiceNames,
addSuffixToServiceNames,
} from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
const composeFileCombinedAllCases = `
version: "3.8"
services:
web:
image: nginx:latest
container_name: web_container
links:
- api
depends_on:
- api
extends: base_service
api:
image: myapi:latest
depends_on:
db:
condition: service_healthy
volumes_from:
- db
db:
image: postgres:latest
base_service:
image: base:latest
networks:
default:
driver: bridge
`;
const expectedComposeFile = parse(`
version: "3.8"
services:
web-testhash:
image: nginx:latest
container_name: web_container-testhash
links:
- api-testhash
depends_on:
- api-testhash
extends: base_service-testhash
api-testhash:
image: myapi:latest
depends_on:
db-testhash:
condition: service_healthy
volumes_from:
- db-testhash
db-testhash:
image: postgres:latest
base_service-testhash:
image: base:latest
networks:
default:
driver: bridge
`);
test("Add suffix to all service names in compose file", () => {
const composeData = parse(
composeFileCombinedAllCases,
) as ComposeSpecification;
const suffix = "testhash";
if (!composeData.services) {
return;
}
const updatedComposeData = addSuffixToServiceNames(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
expect(actualComposeData).toEqual(expectedComposeFile);
});
const composeFile1 = `
version: "3.8"
services:
web:
image: nginx:latest
container_name: web_container
depends_on:
- app
networks:
- frontend
volumes_from:
- data
links:
- db
extends:
service: base_service
app:
image: node:14
networks:
- backend
- frontend
db:
image: postgres:13
networks:
- backend
data:
image: busybox
volumes:
- /data
base_service:
image: base:latest
networks:
frontend:
driver: bridge
backend:
driver: bridge
`;
const expectedComposeFile1 = parse(`
version: "3.8"
services:
web-testhash:
image: nginx:latest
container_name: web_container-testhash
depends_on:
- app-testhash
networks:
- frontend
volumes_from:
- data-testhash
links:
- db-testhash
extends:
service: base_service-testhash
app-testhash:
image: node:14
networks:
- backend
- frontend
db-testhash:
image: postgres:13
networks:
- backend
data-testhash:
image: busybox
volumes:
- /data
base_service-testhash:
image: base:latest
networks:
frontend:
driver: bridge
backend:
driver: bridge
`) as ComposeSpecification;
test("Add suffix to all service names in compose file 1", () => {
const composeData = parse(composeFile1) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllServiceNames(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFile1);
});
const composeFile2 = `
version: "3.8"
services:
frontend:
image: nginx:latest
depends_on:
- backend
networks:
- public
volumes_from:
- logs
links:
- cache
extends:
service: shared_service
backend:
image: node:14
networks:
- private
- public
cache:
image: redis:latest
networks:
- private
logs:
image: busybox
volumes:
- /logs
shared_service:
image: shared:latest
networks:
public:
driver: bridge
private:
driver: bridge
`;
const expectedComposeFile2 = parse(`
version: "3.8"
services:
frontend-testhash:
image: nginx:latest
depends_on:
- backend-testhash
networks:
- public
volumes_from:
- logs-testhash
links:
- cache-testhash
extends:
service: shared_service-testhash
backend-testhash:
image: node:14
networks:
- private
- public
cache-testhash:
image: redis:latest
networks:
- private
logs-testhash:
image: busybox
volumes:
- /logs
shared_service-testhash:
image: shared:latest
networks:
public:
driver: bridge
private:
driver: bridge
`) as ComposeSpecification;
test("Add suffix to all service names in compose file 2", () => {
const composeData = parse(composeFile2) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllServiceNames(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFile2);
});
const composeFile3 = `
version: "3.8"
services:
service_a:
image: service_a:latest
depends_on:
- service_b
networks:
- net_a
volumes_from:
- data_volume
links:
- service_c
extends:
service: common_service
service_b:
image: service_b:latest
networks:
- net_b
- net_a
service_c:
image: service_c:latest
networks:
- net_b
data_volume:
image: busybox
volumes:
- /data
common_service:
image: common:latest
networks:
net_a:
driver: bridge
net_b:
driver: bridge
`;
const expectedComposeFile3 = parse(`
version: "3.8"
services:
service_a-testhash:
image: service_a:latest
depends_on:
- service_b-testhash
networks:
- net_a
volumes_from:
- data_volume-testhash
links:
- service_c-testhash
extends:
service: common_service-testhash
service_b-testhash:
image: service_b:latest
networks:
- net_b
- net_a
service_c-testhash:
image: service_c:latest
networks:
- net_b
data_volume-testhash:
image: busybox
volumes:
- /data
common_service-testhash:
image: common:latest
networks:
net_a:
driver: bridge
net_b:
driver: bridge
`) as ComposeSpecification;
test("Add suffix to all service names in compose file 3", () => {
const composeData = parse(composeFile3) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllServiceNames(composeData, suffix);
expect(updatedComposeData).toEqual(expectedComposeFile3);
});
================================================
FILE: apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
test("Generate random hash with 8 characters", () => {
const hash = generateRandomHash();
expect(hash).toBeDefined();
expect(hash.length).toBe(8);
});
const composeFile3 = `
version: "3.8"
services:
web:
image: nginx:latest
volumes_from:
- shared
api:
image: myapi:latest
volumes_from:
- shared
shared:
image: busybox
volumes:
- /data
networks:
default:
driver: bridge
`;
test("Add suffix to service names with volumes_from in compose file", () => {
const composeData = parse(composeFile3) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData.services) {
return;
}
const updatedComposeData = addSuffixToServiceNames(
composeData.services,
suffix,
);
const actualComposeData = { ...composeData, services: updatedComposeData };
// Verificar que la nueva clave del servicio tiene el prefijo y la vieja clave no existe
expect(actualComposeData.services).toHaveProperty(`web-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("web");
// Verificar que la configuración de la imagen sigue igual
expect(actualComposeData.services?.[`web-${suffix}`]?.image).toBe(
"nginx:latest",
);
expect(actualComposeData.services?.[`api-${suffix}`]?.image).toBe(
"myapi:latest",
);
// Verificar que los nombres en volumes_from tienen el prefijo
expect(actualComposeData.services?.[`web-${suffix}`]?.volumes_from).toContain(
`shared-${suffix}`,
);
expect(actualComposeData.services?.[`api-${suffix}`]?.volumes_from).toContain(
`shared-${suffix}`,
);
// Verificar que el servicio shared también tiene el prefijo
expect(actualComposeData.services).toHaveProperty(`shared-${suffix}`);
expect(actualComposeData.services).not.toHaveProperty("shared");
expect(actualComposeData.services?.[`shared-${suffix}`]?.image).toBe(
"busybox",
);
});
================================================
FILE: apps/dokploy/__test__/compose/volume/volume-2.test.ts
================================================
import type { ComposeSpecification } from "@dokploy/server";
import {
addSuffixToAllVolumes,
addSuffixToVolumesRoot,
generateRandomHash,
} from "@dokploy/server";
import { expect, test } from "vitest";
import { parse } from "yaml";
const composeFile = `
services:
mail:
image: bytemark/smtp
restart: always
plausible_db:
image: postgres:14-alpine
restart: always
volumes:
- db-data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=postgres
plausible_events_db:
image: clickhouse/clickhouse-server:23.3.7.5-alpine
restart: always
volumes:
- event-data:/var/lib/clickhouse
- event-logs:/var/log/clickhouse-server
- ./clickhouse/clickhouse-config.xml:/etc/clickhouse-server/config.d/logging.xml:ro
- ./clickhouse/clickhouse-user-config.xml:/etc/clickhouse-server/users.d/logging.xml:ro
ulimits:
nofile:
soft: 262144
hard: 262144
plausible:
image: plausible/analytics:v2.0
restart: always
command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
depends_on:
- plausible_db
- plausible_events_db
- mail
ports:
- 127.0.0.1:8000:8000
env_file:
- plausible-conf.env
volumes:
- type: volume
source: plausible-data
target: /data
mysql:
image: mysql:5.7
restart: always
environment:
MYSQL_ROOT_PASSWORD: example
volumes:
- type: volume
source: db-data
target: /var/lib/mysql/data
volumes:
db-data:
driver: local
event-data:
driver: local
event-logs:
driver: local
`;
const expectedDockerCompose = parse(`
services:
mail:
image: bytemark/smtp
restart: always
plausible_db:
image: postgres:14-alpine
restart: always
volumes:
- db-data-testhash:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=postgres
plausible_events_db:
image: clickhouse/clickhouse-server:23.3.7.5-alpine
restart: always
volumes:
- event-data-testhash:/var/lib/clickhouse
- event-logs-testhash:/var/log/clickhouse-server
- ./clickhouse/clickhouse-config.xml:/etc/clickhouse-server/config.d/logging.xml:ro
- ./clickhouse/clickhouse-user-config.xml:/etc/clickhouse-server/users.d/logging.xml:ro
ulimits:
nofile:
soft: 262144
hard: 262144
plausible:
image: plausible/analytics:v2.0
restart: always
command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
depends_on:
- plausible_db
- plausible_events_db
- mail
ports:
- 127.0.0.1:8000:8000
env_file:
- plausible-conf.env
volumes:
- type: volume
source: plausible-data-testhash
target: /data
mysql:
image: mysql:5.7
restart: always
environment:
MYSQL_ROOT_PASSWORD: example
volumes:
- type: volume
source: db-data-testhash
target: /var/lib/mysql/data
volumes:
db-data-testhash:
driver: local
event-data-testhash:
driver: local
event-logs-testhash:
driver: local
`) as ComposeSpecification;
test("Generate random hash with 8 characters", () => {
const hash = generateRandomHash();
expect(hash).toBeDefined();
expect(hash.length).toBe(8);
});
// Docker compose needs unique names for services, volumes, networks and containers
// So base on a input which is a dockercompose file, it should replace the name with a hash and return a new dockercompose file
test("Add suffix to volumes root property", () => {
const composeData = parse(composeFile) as ComposeSpecification;
const suffix = generateRandomHash();
if (!composeData?.volumes) {
return;
}
const volumes = addSuffixToVolumesRoot(composeData.volumes, suffix);
// {
// 'db-data-af045046': { driver: 'local' },
// 'event-data-af045046': { driver: 'local' },
// 'event-logs-af045046': { driver: 'local' }
// }
expect(volumes).toBeDefined();
for (const volumeKey of Object.keys(volumes)) {
expect(volumeKey).toContain(`-${suffix}`);
}
});
test("Expect to change the suffix in all the possible places", () => {
const composeData = parse(composeFile) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllVolumes(composeData, suffix);
expect(updatedComposeData).toEqual(expectedDockerCompose);
});
const composeFile2 = `
version: '3.8'
services:
app:
image: myapp:latest
build:
context: .
dockerfile: Dockerfile
volumes:
- app-config:/usr/src/app/config
- ./config:/usr/src/app/config:ro
environment:
- NODE_ENV=production
mongo:
image: mongo:4.2
volumes:
- mongo-data:/data/db
volumes:
app-config:
mongo-data:
`;
const expectedDockerCompose2 = parse(`
version: '3.8'
services:
app:
image: myapp:latest
build:
context: .
dockerfile: Dockerfile
volumes:
- app-config-testhash:/usr/src/app/config
- ./config:/usr/src/app/config:ro
environment:
- NODE_ENV=production
mongo:
image: mongo:4.2
volumes:
- mongo-data-testhash:/data/db
volumes:
app-config-testhash:
mongo-data-testhash:
`) as ComposeSpecification;
test("Expect to change the suffix in all the possible places (2 Try)", () => {
const composeData = parse(composeFile2) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllVolumes(composeData, suffix);
expect(updatedComposeData).toEqual(expectedDockerCompose2);
});
const composeFile3 = `
version: '3.8'
services:
app:
image: myapp:latest
build:
context: .
dockerfile: Dockerfile
volumes:
- app-config:/usr/src/app/config
- ./config:/usr/src/app/config:ro
environment:
- NODE_ENV=production
mongo:
image: mongo:4.2
volumes:
- mongo-data:/data/db
volumes:
app-config:
mongo-data:
`;
const expectedDockerCompose3 = parse(`
version: '3.8'
services:
app:
image: myapp:latest
build:
context: .
dockerfile: Dockerfile
volumes:
- app-config-testhash:/usr/src/app/config
- ./config:/usr/src/app/config:ro
environment:
- NODE_ENV=production
mongo:
image: mongo:4.2
volumes:
- mongo-data-testhash:/data/db
volumes:
app-config-testhash:
mongo-data-testhash:
`) as ComposeSpecification;
test("Expect to change the suffix in all the possible places (3 Try)", () => {
const composeData = parse(composeFile3) as ComposeSpecification;
const suffix = "testhash";
const updatedComposeData = addSuffixToAllVolumes(composeData, suffix);
expect(updatedComposeData).toEqual(expectedDockerCompose3);
});
const composeFileComplex = `
version: "3.8"
services:
studio:
container_name: supabase-studio
image: supabase/studio:20240422-5cf8f30
restart: unless-stopped
healthcheck:
test:
[
"CMD",
"node",
"-e",
"require('http').get('http://localhost:3000/api/profile', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
]
timeout: 5s
interval: 5s
retries: 3
depends_on:
analytics:
condition: service_healthy
environment:
STUDIO_PG_META_URL: http://meta:8080
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD}
DEFAULT_ORGANIZATION_NAME: \${STUDIO_DEFAULT_ORGANIZATION}
DEFAULT_PROJECT_NAME: \${STUDIO_DEFAULT_PROJECT}
SUPABASE_URL: http://kong:8000
SUPABASE_PUBLIC_URL: \${SUPABASE_PUBLIC_URL}
SUPABASE_ANON_KEY: \${ANON_KEY}
SUPABASE_SERVICE_KEY: \${SERVICE_ROLE_KEY}
LOGFLARE_API_KEY: \${LOGFLARE_API_KEY}
LOGFLARE_URL: http://analytics:4000
NEXT_PUBLIC_ENABLE_LOGS: true
NEXT_ANALYTICS_BACKEND_PROVIDER: postgres
kong:
container_name: supabase-kong
image: kong:2.8.1
restart: unless-stopped
entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start'
ports:
- \${KONG_HTTP_PORT}:8000/tcp
- \${KONG_HTTPS_PORT}:8443/tcp
depends_on:
analytics:
condition: service_healthy
environment:
KONG_DATABASE: "off"
KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml
KONG_DNS_ORDER: LAST,A,CNAME
KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth
KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
SUPABASE_ANON_KEY: \${ANON_KEY}
SUPABASE_SERVICE_KEY: \${SERVICE_ROLE_KEY}
DASHBOARD_USERNAME: \${DASHBOARD_USERNAME}
DASHBOARD_PASSWORD: \${DASHBOARD_PASSWORD}
volumes:
- ./volumes/api/kong.yml:/home/kong/temp.yml:ro
auth:
container_name: supabase-auth
image: supabase/gotrue:v2.151.0
depends_on:
db:
condition: service_healthy
analytics:
condition: service_healthy
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost:9999/health"
]
timeout: 5s
interval: 5s
retries: 3
restart: unless-stopped
environment:
GOTRUE_API_HOST: 0.0.0.0
GOTRUE_API_PORT: 9999
API_EXTERNAL_URL: \${API_EXTERNAL_URL}
GOTRUE_DB_DRIVER: postgres
GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:\${POSTGRES_PASSWORD}@\${POSTGRES_HOST}:\${POSTGRES_PORT}/\${POSTGRES_DB}
GOTRUE_SITE_URL: \${SITE_URL}
GOTRUE_URI_ALLOW_LIST: \${ADDITIONAL_REDIRECT_URLS}
GOTRUE_DISABLE_SIGNUP: \${DISABLE_SIGNUP}
GOTRUE_JWT_ADMIN_ROLES: service_role
GOTRUE_JWT_AUD: authenticated
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
GOTRUE_JWT_EXP: \${JWT_EXPIRY}
GOTRUE_JWT_SECRET: \${JWT_SECRET}
GOTRUE_EXTERNAL_EMAIL_ENABLED: \${ENABLE_EMAIL_SIGNUP}
GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: \${ENABLE_ANONYMOUS_USERS}
GOTRUE_MAILER_AUTOCONFIRM: \${ENABLE_EMAIL_AUTOCONFIRM}
GOTRUE_SMTP_ADMIN_EMAIL: \${SMTP_ADMIN_EMAIL}
GOTRUE_SMTP_HOST: \${SMTP_HOST}
GOTRUE_SMTP_PORT: \${SMTP_PORT}
GOTRUE_SMTP_USER: \${SMTP_USER}
GOTRUE_SMTP_PASS: \${SMTP_PASS}
GOTRUE_SMTP_SENDER_NAME: \${SMTP_SENDER_NAME}
GOTRUE_MAILER_URLPATHS_INVITE: \${MAILER_URLPATHS_INVITE}
GOTRUE_MAILER_URLPATHS_CONFIRMATION: \${MAILER_URLPATHS_CONFIRMATION}
GOTRUE_MAILER_URLPATHS_RECOVERY: \${MAILER_URLPATHS_RECOVERY}
GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: \${MAILER_URLPATHS_EMAIL_CHANGE}
GOTRUE_EXTERNAL_PHONE_ENABLED: \${ENABLE_PHONE_SIGNUP}
GOTRUE_SMS_AUTOCONFIRM: \${ENABLE_PHONE_AUTOCONFIRM}
rest:
container_name: supabase-rest
image: postgrest/postgrest:v12.0.1
depends_on:
db:
condition: service_healthy
analytics:
condition: service_healthy
restart: unless-stopped
environment:
PGRST_DB_URI: postgres://authenticator:\${POSTGRES_PASSWORD}@\${POSTGRES_HOST}:\${POSTGRES_PORT}/\${POSTGRES_DB}
PGRST_DB_SCHEMAS: \${PGRST_DB_SCHEMAS}
PGRST_DB_ANON_ROLE: anon
PGRST_JWT_SECRET: \${JWT_SECRET}
PGRST_DB_USE_LEGACY_GUCS: "false"
PGRST_APP_SETTINGS_JWT_SECRET: \${JWT_SECRET}
PGRST_APP_SETTINGS_JWT_EXP: \${JWT_EXPIRY}
command: "postgrest"
realtime:
container_name: realtime-dev.supabase-realtime
image: supabase/realtime:v2.28.32
depends_on:
db:
condition: service_healthy
analytics:
condition: service_healthy
healthcheck:
test:
[
"CMD",
"curl",
"-sSfL",
"--head",
"-o",
"/dev/null",
"-H",
"Authorization: Bearer \${ANON_KEY}",
"http://localhost:4000/api/tenants/realtime-dev/health"
]
timeout: 5s
interval: 5s
retries: 3
restart: unless-stopped
environment:
PORT: 4000
DB_HOST: \${POSTGRES_HOST}
DB_PORT: \${POSTGRES_PORT}
DB_USER: supabase_admin
DB_PASSWORD: \${POSTGRES_PASSWORD}
DB_NAME: \${POSTGRES_DB}
DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime'
DB_ENC_KEY: supabaserealtime
API_JWT_SECRET: \${JWT_SECRET}
FLY_ALLOC_ID: fly123
FLY_APP_NAME: realtime
SECRET_KEY_BASE: UpNVntn3cDxHJpq99YMc1T1AQgQpc8kfYTuRgBiYa15BLrx8etQoXz3gZv1/u2oq
ERL_AFLAGS: -proto_dist inet_tcp
ENABLE_TAILSCALE: "false"
DNS_NODES: "''"
command: >
sh -c "/app/bin/migrate && /app/bin/realtime eval 'Realtime.Release.seeds(Realtime.Repo)' && /app/bin/server"
storage:
container_name: supabase-storage
image: supabase/storage-api:v1.0.6
depends_on:
db:
condition: service_healthy
rest:
condition: service_started
imgproxy:
condition: service_started
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost:5000/status"
]
timeout: 5s
interval: 5s
retries: 3
restart: unless-stopped
environment:
ANON_KEY: \${ANON_KEY}
SERVICE_KEY: \${SERVICE_ROLE_KEY}
POSTGREST_URL: http://rest:3000
PGRST_JWT_SECRET: \${JWT_SECRET}
DATABASE_URL: postgres://supabase_storage_admin:\${POSTGRES_PASSWORD}@\${POSTGRES_HOST}:\${POSTGRES_PORT}/\${POSTGRES_DB}
FILE_SIZE_LIMIT: 52428800
STORAGE_BACKEND: file
FILE_STORAGE_BACKEND_PATH: /var/lib/storage
TENANT_ID: stub
REGION: stub
GLOBAL_S3_BUCKET: stub
ENABLE_IMAGE_TRANSFORMATION: "true"
IMGPROXY_URL: http://imgproxy:5001
volumes:
- ./volumes/storage:/var/lib/storage:z
imgproxy:
container_name: supabase-imgproxy
image: darthsim/imgproxy:v3.8.0
healthcheck:
test: [ "CMD", "imgproxy", "health" ]
timeout: 5s
interval: 5s
retries: 3
environment:
IMGPROXY_BIND: ":5001"
IMGPROXY_LOCAL_FILESYSTEM_ROOT: /
IMGPROXY_USE_ETAG: "true"
IMGPROXY_ENABLE_WEBP_DETECTION: \${IMGPROXY_ENABLE_WEBP_DETECTION}
volumes:
- ./volumes/storage:/var/lib/storage:z
meta:
container_name: supabase-meta
image: supabase/postgres-meta:v0.80.0
depends_on:
db:
condition: service_healthy
analytics:
condition: service_healthy
restart: unless-stopped
environment:
PG_META_PORT: 8080
PG_META_DB_HOST: \${POSTGRES_HOST}
PG_META_DB_PORT: \${POSTGRES_PORT}
PG_META_DB_NAME: \${POSTGRES_DB}
PG_META_DB_USER: supabase_admin
PG_META_DB_PASSWORD: \${POSTGRES_PASSWORD}
functions:
container_name: supabase-edge-functions
image: supabase/edge-runtime:v1.45.2
restart: unless-stopped
depends_on:
analytics:
condition: service_healthy
environment:
JWT_SECRET: \${JWT_SECRET}
SUPABASE_URL: http://kong:8000
SUPABASE_ANON_KEY: \${ANON_KEY}
SUPABASE_SERVICE_ROLE_KEY: \${SERVICE_ROLE_KEY}
SUPABASE_DB_URL: postgresql://postgres:\${POSTGRES_PASSWORD}@\${POSTGRES_HOST}:\${POSTGRES_PORT}/\${POSTGRES_DB}
VERIFY_JWT: "\${FUNCTIONS_VERIFY_JWT}"
volumes:
- ./volumes/functions:/home/deno/functions:Z
command:
- start
- --main-service
- /home/deno/functions/main
analytics:
container_name: supabase-analytics
image: supabase/logflare:1.4.0
healthcheck:
test: [ "CMD", "curl", "http://localhost:4000/health" ]
timeout: 5s
interval: 5s
retries: 10
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
LOGFLARE_NODE_HOST: 127.0.0.1
DB_USERNAME: supabase_admin
DB_DATABASE: \${POSTGRES_DB}
DB_HOSTNAME: \${POSTGRES_HOST}
DB_PORT: \${POSTGRES_PORT}
DB_PASSWORD: \${POSTGRES_PASSWORD}
DB_SCHEMA: _analytics
LOGFLARE_API_KEY: \${LOGFLARE_API_KEY}
LOGFLARE_SINGLE_TENANT: true
LOGFLARE_SUPABASE_MODE: true
LOGFLARE_MIN_CLUSTER_SIZE: 1
POSTGRES_BACKEND_URL: postgresql://supabase_admin:\${POSTGRES_PASSWORD}@\${POSTGRES_HOST}:\${POSTGRES_PORT}/\${POSTGRES_DB}
POSTGRES_BACKEND_SCHEMA: _analytics
LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true
ports:
- 4000:4000
db:
container_name: supabase-db
image: supabase/postgres:15.1.1.41
healthcheck:
test: pg_isready -U postgres -h localhost
interval: 5s
timeout: 5s
retries: 10
depends_on:
vector:
condition: service_healthy
command:
- postgres
- -c
- config_file=/etc/postgresql/postgresql.conf
- -c
- log_min_messages=fatal
restart: unless-stopped
ports:
- \${POSTGRES_PORT}:\${POSTGRES_PORT}
environment:
POSTGRES_HOST: /var/run/postgresql
PGPORT: \${POSTGRES_PORT}
POSTGRES_PORT: \${POSTGRES_PORT}
PGPASSWORD: \${POSTGRES_PASSWORD}
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD}
PGDATABASE: \${POSTGRES_DB}
POSTGRES_DB: \${POSTGRES_DB}
JWT_SECRET: \${JWT_SECRET}
JWT_EXP: \${JWT_EXPIRY}
volumes:
- ./volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z
- ./volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z
- ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z
- ./volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z
- ./volumes/db/data:/var/lib/postgresql/data:Z
- ./volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z
- db-config:/etc/postgresql-custom
vector:
container_name: supabase-vector
image: timberio/vector:0.28.1-alpine
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://vector:9001/health"
]
timeout: 5s
interval: 5s
retries: 3
volumes:
- ./volumes/logs/vector.yml:/etc/vector/vector.yml:ro
- "\${DOCKER_SOCKET_LOCATION}:/var/run/docker.sock:ro"
environment:
LOGFLARE_API_KEY: \${LOGFLARE_API_KEY}
command: [ "--config", "etc/vector/vector.yml" ]
volumes:
db-config:
`;
const expectedDockerComposeComplex = parse(`
version: "3.8"
services:
studio:
container_name: supabase-studio
image: supabase/studio:20240422-5cf8f30
restart: unless-stopped
healthcheck:
test:
[
"CMD",
"node",
"-e",
"require('http').get('http://localhost:3000/api/profile', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
]
timeout: 5s
interval: 5s
retries: 3
depends_on:
analytics:
condition: service_healthy
environment:
STUDIO_PG_META_URL: http://meta:8080
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD}
DEFAULT_ORGANIZATION_NAME: \${STUDIO_DEFAULT_ORGANIZATION}
DEFAULT_PROJECT_NAME: \${STUDIO_DEFAULT_PROJECT}
SUPABASE_URL: http://kong:8000
SUPABASE_PUBLIC_URL: \${SUPABASE_PUBLIC_URL}
SUPABASE_ANON_KEY: \${ANON_KEY}
SUPABASE_SERVICE_KEY: \${SERVICE_ROLE_KEY}
LOGFLARE_API_KEY: \${LOGFLARE_API_KEY}
LOGFLARE_URL: http://analytics:4000
NEXT_PUBLIC_ENABLE_LOGS: true
NEXT_ANALYTICS_BACKEND_PROVIDER: postgres
kong:
container_name: supabase-kong
image: kong:2.8.1
restart: unless-stopped
entrypoint: bash -c 'eval "echo \"$$(cat ~/temp.yml)\"" > ~/kong.yml && /docker-entrypoint.sh kong docker-start'
ports:
- \${KONG_HTTP_PORT}:8000/tcp
- \${KONG_HTTPS_PORT}:8443/tcp
depends_on:
analytics:
condition: service_healthy
environment:
KONG_DATABASE: "off"
KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml
KONG_DNS_ORDER: LAST,A,CNAME
KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth
KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
SUPABASE_ANON_KEY: \${ANON_KEY}
SUPABASE_SERVICE_KEY: \${SERVICE_ROLE_KEY}
DASHBOARD_USERNAME: \${DASHBOARD_USERNAME}
DASHBOARD_PASSWORD: \${DASHBOARD_PASSWORD}
volumes:
- ./volumes/api/kong.yml:/home/kong/temp.yml:ro
auth:
container_name: supabase-auth
image: supabase/gotrue:v2.151.0
depends_on:
db:
condition: service_healthy
analytics:
condition: service_healthy
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost:9999/health"
]
timeout: 5s
interval: 5s
retries: 3
restart: unless-stopped
environment:
GOTRUE_API_HOST: 0.0.0.0
GOTRUE_API_PORT: 9999
API_EXTERNAL_URL: \${API_EXTERNAL_URL}
GOTRUE_DB_DRIVER: postgres
GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:\${POSTGRES_PASSWORD}@\${POSTGRES_HOST}:\${POSTGRES_PORT}/\${POSTGRES_DB}
GOTRUE_SITE_URL: \${SITE_URL}
GOTRUE_URI_ALLOW_LIST: \${ADDITIONAL_REDIRECT_URLS}
GOTRUE_DISABLE_SIGNUP: \${DISABLE_SIGNUP}
GOTRUE_JWT_ADMIN_ROLES: service_role
GOTRUE_JWT_AUD: authenticated
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
GOTRUE_JWT_EXP: \${JWT_EXPIRY}
GOTRUE_JWT_SECRET: \${JWT_SECRET}
GOTRUE_EXTERNAL_EMAIL_ENABLED: \${ENABLE_EMAIL_SIGNUP}
GOTRUE_EXTERNAL_ANONYMOUS_USERS_ENABLED: \${ENABLE_ANONYMOUS_USERS}
GOTRUE_MAILER_AUTOCONFIRM: \${ENABLE_EMAIL_AUTOCONFIRM}
GOTRUE_SMTP_ADMIN_EMAIL: \${SMTP_ADMIN_EMAIL}
GOTRUE_SMTP_HOST: \${SMTP_HOST}
GOTRUE_SMTP_PORT: \${SMTP_PORT}
GOTRUE_SMTP_USER: \${SMTP_USER}
GOTRUE_SMTP_PASS: \${SMTP_PASS}
GOTRUE_SMTP_SENDER_NAME: \${SMTP_SENDER_NAME}
GOTRUE_MAILER_URLPATHS_INVITE: \${MAILER_URLPATHS_INVITE}
GOTRUE_MAILER_URLPATHS_CONFIRMATION: \${MAILER_URLPATHS_CONFIRMATION}
GOTRUE_MAILER_URLPATHS_RECOVERY: \${MAILER_URLPATHS_RECOVERY}
GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: \${MAILER_URLPATHS_EMAIL_CHANGE}
GOTRUE_EXTERNAL_PHONE_ENABLED: \${ENABLE_PHONE_SIGNUP}
GOTRUE_SMS_AUTOCONFIRM: \${ENABLE_PHONE_AUTOCONFIRM}
rest:
container_name: supabase-rest
image: postgrest/postgrest:v12.0.1
depends_on:
db:
condition: service_healthy
analytics:
condition: service_healthy
restart: unless-stopped
environment:
PGRST_DB_URI: postgres://authenticator:\${POSTGRES_PASSWORD}@\${POSTGRES_HOST}:\${POSTGRES_PORT}/\${POSTGRES_DB}
PGRST_DB_SCHEMAS: \${PGRST_DB_SCHEMAS}
PGRST_DB_ANON_ROLE: anon
PGRST_JWT_SECRET: \${JWT_SECRET}
PGRST_DB_USE_LEGACY_GUCS: "false"
PGRST_APP_SETTINGS_JWT_SECRET: \${JWT_SECRET}
PGRST_APP_SETTINGS_JWT_EXP: \${JWT_EXPIRY}
command: "postgrest"
realtime:
container_name: realtime-dev.supabase-realtime
image: supabase/realtime:v2.28.32
depends_on:
db:
condition: service_healthy
analytics:
condition: service_healthy
healthcheck:
test:
[
"CMD",
"curl",
"-sSfL",
"--head",
"-o",
"/dev/null",
"-H",
"Authorization: Bearer \${ANON_KEY}",
"http://localhost:4000/api/tenants/realtime-dev/health"
]
timeout: 5s
interval: 5s
retries: 3
restart: unless-stopped
environment:
PORT: 4000
DB_HOST: \${POSTGRES_HOST}
DB_PORT: \${POSTGRES_PORT}
DB_USER: supabase_admin
DB_PASSWORD: \${POSTGRES_PASSWORD}
DB_NAME: \${POSTGRES_DB}
DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime'
DB_ENC_KEY: supabaserealtime
API_JWT_SECRET: \${JWT_SECRET}
FLY_ALLOC_ID: fly123
FLY_APP_NAME: realtime
SECRET_KEY_BASE: UpNVntn3cDxHJpq99YMc1T1AQgQpc8kfYTuRgBiYa15BLrx8etQoXz3gZv1/u2oq
ERL_AFLAGS: -proto_dist inet_tcp
ENABLE_TAILSCALE: "false"
DNS_NODES: "''"
command: >
sh -c "/app/bin/migrate && /app/bin/realtime eval 'Realtime.Release.seeds(Realtime.Repo)' && /app/bin/server"
storage:
container_name: supabase-storage
image: supabase/storage-api:v1.0.6
depends_on:
db:
condition: service_healthy
rest:
condition: service_started
imgproxy:
condition: service_started
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost:5000/status"
]
timeout: 5s
interval: 5s
retries: 3
restart: unless-stopped
environment:
ANON_KEY: \${ANON_KEY}
SERVICE_KEY: \${SERVICE_ROLE_KEY}
POSTGREST_URL: http://rest:3000
PGRST_JWT_SECRET: \${JWT_SECRET}
DATABASE_URL: postgres://supabase_storage_admin:\${POSTGRES_PASSWORD}@\${POSTGRES_HOST}:\${POSTGRES_PORT}/\${POSTGRES_DB}
FILE_SIZE_LIMIT:
gitextract_so1vxlep/ ├── .devcontainer/ │ ├── Dockerfile │ └── devcontainer.json ├── .dockerignore ├── .github/ │ ├── CODEOWNERS │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ └── feature-request.yml │ ├── pull_request_template.md │ └── workflows/ │ ├── create-pr.yml │ ├── deploy.yml │ ├── dokploy.yml │ ├── format.yml │ ├── monitoring.yml │ ├── pr-quality.yml │ ├── pull-request.yml │ └── sync-openapi-docs.yml ├── .gitignore ├── .nvmrc ├── .vscode/ │ ├── extensions.json │ └── settings.json ├── CONTRIBUTING.md ├── Dockerfile ├── Dockerfile.cloud ├── Dockerfile.monitoring ├── Dockerfile.schedule ├── Dockerfile.server ├── GUIDES.md ├── LICENSE.MD ├── LICENSE_PROPRIETARY.md ├── README.md ├── SECURITY.md ├── TERMS_AND_CONDITIONS.md ├── apps/ │ ├── api/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── logger.ts │ │ │ ├── schema.ts │ │ │ ├── service.ts │ │ │ └── utils.ts │ │ └── tsconfig.json │ ├── dokploy/ │ │ ├── .dockerignore │ │ ├── .gitignore │ │ ├── __test__/ │ │ │ ├── cluster/ │ │ │ │ └── upload.test.ts │ │ │ ├── compose/ │ │ │ │ ├── compose.test.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config-root.test.ts │ │ │ │ │ ├── config-service.test.ts │ │ │ │ │ └── config.test.ts │ │ │ │ ├── domain/ │ │ │ │ │ ├── host-rule-format.test.ts │ │ │ │ │ ├── labels.test.ts │ │ │ │ │ ├── network-root.test.ts │ │ │ │ │ └── network-service.test.ts │ │ │ │ ├── network/ │ │ │ │ │ ├── network-root.test.ts │ │ │ │ │ ├── network-service.test.ts │ │ │ │ │ └── network.test.ts │ │ │ │ ├── secrets/ │ │ │ │ │ ├── secret-root.test.ts │ │ │ │ │ ├── secret-services.test.ts │ │ │ │ │ └── secret.test.ts │ │ │ │ ├── service/ │ │ │ │ │ ├── service-container-name.test.ts │ │ │ │ │ ├── service-depends-on.test.ts │ │ │ │ │ ├── service-extends.test.ts │ │ │ │ │ ├── service-links.test.ts │ │ │ │ │ ├── service-names.test.ts │ │ │ │ │ ├── service.test.ts │ │ │ │ │ └── sevice-volumes-from.test.ts │ │ │ │ └── volume/ │ │ │ │ ├── volume-2.test.ts │ │ │ │ ├── volume-root.test.ts │ │ │ │ ├── volume-services.test.ts │ │ │ │ └── volume.test.ts │ │ │ ├── deploy/ │ │ │ │ ├── application.command.test.ts │ │ │ │ ├── application.real.test.ts │ │ │ │ ├── github.test.ts │ │ │ │ └── soft-serve.test.ts │ │ │ ├── drop/ │ │ │ │ ├── drop.test.ts │ │ │ │ └── zips/ │ │ │ │ ├── folder1/ │ │ │ │ │ └── folder1.txt │ │ │ │ ├── folder2/ │ │ │ │ │ └── folder2.txt │ │ │ │ ├── folder3/ │ │ │ │ │ └── file3.txt │ │ │ │ └── test.txt │ │ │ ├── env/ │ │ │ │ ├── environment-access-fallback.test.ts │ │ │ │ ├── environment.test.ts │ │ │ │ ├── shared.test.ts │ │ │ │ └── stack-environment.test.ts │ │ │ ├── permissions/ │ │ │ │ ├── check-permission.test.ts │ │ │ │ ├── enterprise-only-resources.test.ts │ │ │ │ ├── resolve-permissions.test.ts │ │ │ │ └── service-access.test.ts │ │ │ ├── requests/ │ │ │ │ └── request.test.ts │ │ │ ├── server/ │ │ │ │ └── mechanizeDockerContainer.test.ts │ │ │ ├── setup.ts │ │ │ ├── templates/ │ │ │ │ ├── config.template.test.ts │ │ │ │ └── helpers.template.test.ts │ │ │ ├── traefik/ │ │ │ │ ├── server/ │ │ │ │ │ └── update-server-config.test.ts │ │ │ │ └── traefik.test.ts │ │ │ ├── utils/ │ │ │ │ └── backups.test.ts │ │ │ ├── vitest.config.ts │ │ │ └── wss/ │ │ │ ├── readValidDirectory.test.ts │ │ │ └── utils.test.ts │ │ ├── components/ │ │ │ ├── dashboard/ │ │ │ │ ├── application/ │ │ │ │ │ ├── advanced/ │ │ │ │ │ │ ├── cluster/ │ │ │ │ │ │ │ ├── modify-swarm-settings.tsx │ │ │ │ │ │ │ ├── show-cluster-settings.tsx │ │ │ │ │ │ │ └── swarm-forms/ │ │ │ │ │ │ │ ├── endpoint-spec-form.tsx │ │ │ │ │ │ │ ├── health-check-form.tsx │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── labels-form.tsx │ │ │ │ │ │ │ ├── mode-form.tsx │ │ │ │ │ │ │ ├── network-form.tsx │ │ │ │ │ │ │ ├── placement-form.tsx │ │ │ │ │ │ │ ├── restart-policy-form.tsx │ │ │ │ │ │ │ ├── rollback-config-form.tsx │ │ │ │ │ │ │ ├── stop-grace-period-form.tsx │ │ │ │ │ │ │ ├── update-config-form.tsx │ │ │ │ │ │ │ └── utils.ts │ │ │ │ │ │ ├── general/ │ │ │ │ │ │ │ └── add-command.tsx │ │ │ │ │ │ ├── import/ │ │ │ │ │ │ │ └── show-import.tsx │ │ │ │ │ │ ├── ports/ │ │ │ │ │ │ │ ├── handle-ports.tsx │ │ │ │ │ │ │ └── show-port.tsx │ │ │ │ │ │ ├── redirects/ │ │ │ │ │ │ │ ├── handle-redirect.tsx │ │ │ │ │ │ │ └── show-redirects.tsx │ │ │ │ │ │ ├── security/ │ │ │ │ │ │ │ ├── handle-security.tsx │ │ │ │ │ │ │ └── show-security.tsx │ │ │ │ │ │ ├── show-build-server.tsx │ │ │ │ │ │ ├── show-resources.tsx │ │ │ │ │ │ ├── traefik/ │ │ │ │ │ │ │ ├── show-traefik-config.tsx │ │ │ │ │ │ │ └── update-traefik-config.tsx │ │ │ │ │ │ └── volumes/ │ │ │ │ │ │ ├── add-volumes.tsx │ │ │ │ │ │ ├── show-volumes.tsx │ │ │ │ │ │ └── update-volume.tsx │ │ │ │ │ ├── build/ │ │ │ │ │ │ └── show.tsx │ │ │ │ │ ├── deployments/ │ │ │ │ │ │ ├── cancel-queues.tsx │ │ │ │ │ │ ├── clear-deployments.tsx │ │ │ │ │ │ ├── kill-build.tsx │ │ │ │ │ │ ├── refresh-token.tsx │ │ │ │ │ │ ├── show-deployment.tsx │ │ │ │ │ │ ├── show-deployments-modal.tsx │ │ │ │ │ │ └── show-deployments.tsx │ │ │ │ │ ├── domains/ │ │ │ │ │ │ ├── dns-helper-modal.tsx │ │ │ │ │ │ ├── handle-domain.tsx │ │ │ │ │ │ └── show-domains.tsx │ │ │ │ │ ├── environment/ │ │ │ │ │ │ ├── show-enviroment.tsx │ │ │ │ │ │ └── show.tsx │ │ │ │ │ ├── general/ │ │ │ │ │ │ ├── generic/ │ │ │ │ │ │ │ ├── save-bitbucket-provider.tsx │ │ │ │ │ │ │ ├── save-docker-provider.tsx │ │ │ │ │ │ │ ├── save-drag-n-drop.tsx │ │ │ │ │ │ │ ├── save-git-provider.tsx │ │ │ │ │ │ │ ├── save-gitea-provider.tsx │ │ │ │ │ │ │ ├── save-github-provider.tsx │ │ │ │ │ │ │ ├── save-gitlab-provider.tsx │ │ │ │ │ │ │ ├── show.tsx │ │ │ │ │ │ │ └── unauthorized-git-provider.tsx │ │ │ │ │ │ └── show.tsx │ │ │ │ │ ├── logs/ │ │ │ │ │ │ └── show.tsx │ │ │ │ │ ├── patches/ │ │ │ │ │ │ ├── create-file-dialog.tsx │ │ │ │ │ │ ├── edit-patch-dialog.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── patch-editor.tsx │ │ │ │ │ │ └── show-patches.tsx │ │ │ │ │ ├── preview-deployments/ │ │ │ │ │ │ ├── add-preview-domain.tsx │ │ │ │ │ │ ├── show-preview-deployments.tsx │ │ │ │ │ │ └── show-preview-settings.tsx │ │ │ │ │ ├── rollbacks/ │ │ │ │ │ │ ├── Backup │ │ │ │ │ │ └── show-rollback-settings.tsx │ │ │ │ │ ├── schedules/ │ │ │ │ │ │ ├── handle-schedules.tsx │ │ │ │ │ │ ├── show-schedules.tsx │ │ │ │ │ │ └── timezones.ts │ │ │ │ │ ├── update-application.tsx │ │ │ │ │ └── volume-backups/ │ │ │ │ │ ├── handle-volume-backups.tsx │ │ │ │ │ ├── restore-volume-backups.tsx │ │ │ │ │ └── show-volume-backups.tsx │ │ │ │ ├── compose/ │ │ │ │ │ ├── advanced/ │ │ │ │ │ │ ├── add-command.tsx │ │ │ │ │ │ └── add-isolation.tsx │ │ │ │ │ ├── delete-service.tsx │ │ │ │ │ ├── general/ │ │ │ │ │ │ ├── actions.tsx │ │ │ │ │ │ ├── compose-file-editor.tsx │ │ │ │ │ │ ├── generic/ │ │ │ │ │ │ │ ├── save-bitbucket-provider-compose.tsx │ │ │ │ │ │ │ ├── save-git-provider-compose.tsx │ │ │ │ │ │ │ ├── save-gitea-provider-compose.tsx │ │ │ │ │ │ │ ├── save-github-provider-compose.tsx │ │ │ │ │ │ │ ├── save-gitlab-provider-compose.tsx │ │ │ │ │ │ │ └── show.tsx │ │ │ │ │ │ ├── randomize-compose.tsx │ │ │ │ │ │ ├── show-converted-compose.tsx │ │ │ │ │ │ └── show.tsx │ │ │ │ │ ├── logs/ │ │ │ │ │ │ ├── show-stack.tsx │ │ │ │ │ │ └── show.tsx │ │ │ │ │ └── update-compose.tsx │ │ │ │ ├── database/ │ │ │ │ │ └── backups/ │ │ │ │ │ ├── handle-backup.tsx │ │ │ │ │ ├── restore-backup.tsx │ │ │ │ │ └── show-backups.tsx │ │ │ │ ├── deployments/ │ │ │ │ │ ├── show-deployments-table.tsx │ │ │ │ │ └── show-queue-table.tsx │ │ │ │ ├── docker/ │ │ │ │ │ ├── config/ │ │ │ │ │ │ └── show-container-config.tsx │ │ │ │ │ ├── logs/ │ │ │ │ │ │ ├── docker-logs-id.tsx │ │ │ │ │ │ ├── line-count-filter.tsx │ │ │ │ │ │ ├── show-docker-modal-logs.tsx │ │ │ │ │ │ ├── show-docker-modal-stack-logs.tsx │ │ │ │ │ │ ├── since-logs-filter.tsx │ │ │ │ │ │ ├── status-logs-filter.tsx │ │ │ │ │ │ ├── terminal-line.tsx │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── show/ │ │ │ │ │ │ ├── colums.tsx │ │ │ │ │ │ └── show-containers.tsx │ │ │ │ │ └── terminal/ │ │ │ │ │ ├── docker-terminal-modal.tsx │ │ │ │ │ └── docker-terminal.tsx │ │ │ │ ├── file-system/ │ │ │ │ │ ├── show-traefik-file.tsx │ │ │ │ │ └── show-traefik-system.tsx │ │ │ │ ├── impersonation/ │ │ │ │ │ └── impersonation-bar.tsx │ │ │ │ ├── mariadb/ │ │ │ │ │ ├── general/ │ │ │ │ │ │ ├── show-external-mariadb-credentials.tsx │ │ │ │ │ │ ├── show-general-mariadb.tsx │ │ │ │ │ │ └── show-internal-mariadb-credentials.tsx │ │ │ │ │ └── update-mariadb.tsx │ │ │ │ ├── mongo/ │ │ │ │ │ ├── general/ │ │ │ │ │ │ ├── show-external-mongo-credentials.tsx │ │ │ │ │ │ ├── show-general-mongo.tsx │ │ │ │ │ │ └── show-internal-mongo-credentials.tsx │ │ │ │ │ └── update-mongo.tsx │ │ │ │ ├── monitoring/ │ │ │ │ │ ├── free/ │ │ │ │ │ │ └── container/ │ │ │ │ │ │ ├── docker-block-chart.tsx │ │ │ │ │ │ ├── docker-cpu-chart.tsx │ │ │ │ │ │ ├── docker-disk-chart.tsx │ │ │ │ │ │ ├── docker-memory-chart.tsx │ │ │ │ │ │ ├── docker-network-chart.tsx │ │ │ │ │ │ ├── show-free-compose-monitoring.tsx │ │ │ │ │ │ └── show-free-container-monitoring.tsx │ │ │ │ │ └── paid/ │ │ │ │ │ ├── container/ │ │ │ │ │ │ ├── container-block-chart.tsx │ │ │ │ │ │ ├── container-cpu-chart.tsx │ │ │ │ │ │ ├── container-memory-chart.tsx │ │ │ │ │ │ ├── container-network-chart.tsx │ │ │ │ │ │ ├── show-paid-compose-monitoring.tsx │ │ │ │ │ │ └── show-paid-container-monitoring.tsx │ │ │ │ │ └── servers/ │ │ │ │ │ ├── cpu-chart.tsx │ │ │ │ │ ├── disk-chart.tsx │ │ │ │ │ ├── memory-chart.tsx │ │ │ │ │ ├── network-chart.tsx │ │ │ │ │ └── show-paid-monitoring.tsx │ │ │ │ ├── mysql/ │ │ │ │ │ ├── general/ │ │ │ │ │ │ ├── show-external-mysql-credentials.tsx │ │ │ │ │ │ ├── show-general-mysql.tsx │ │ │ │ │ │ └── show-internal-mysql-credentials.tsx │ │ │ │ │ └── update-mysql.tsx │ │ │ │ ├── organization/ │ │ │ │ │ └── handle-organization.tsx │ │ │ │ ├── postgres/ │ │ │ │ │ ├── advanced/ │ │ │ │ │ │ └── show-custom-command.tsx │ │ │ │ │ ├── general/ │ │ │ │ │ │ ├── show-external-postgres-credentials.tsx │ │ │ │ │ │ ├── show-general-postgres.tsx │ │ │ │ │ │ └── show-internal-postgres-credentials.tsx │ │ │ │ │ └── update-postgres.tsx │ │ │ │ ├── project/ │ │ │ │ │ ├── add-ai-assistant.tsx │ │ │ │ │ ├── add-application.tsx │ │ │ │ │ ├── add-compose.tsx │ │ │ │ │ ├── add-database.tsx │ │ │ │ │ ├── add-template.tsx │ │ │ │ │ ├── advanced-environment-selector.tsx │ │ │ │ │ ├── ai/ │ │ │ │ │ │ ├── step-one.tsx │ │ │ │ │ │ ├── step-three.tsx │ │ │ │ │ │ ├── step-two.tsx │ │ │ │ │ │ └── template-generator.tsx │ │ │ │ │ ├── duplicate-project.tsx │ │ │ │ │ └── environment-variables.tsx │ │ │ │ ├── projects/ │ │ │ │ │ ├── handle-project.tsx │ │ │ │ │ ├── project-environment.tsx │ │ │ │ │ └── show.tsx │ │ │ │ ├── redis/ │ │ │ │ │ ├── general/ │ │ │ │ │ │ ├── show-external-redis-credentials.tsx │ │ │ │ │ │ ├── show-general-redis.tsx │ │ │ │ │ │ └── show-internal-redis-credentials.tsx │ │ │ │ │ └── update-redis.tsx │ │ │ │ ├── requests/ │ │ │ │ │ ├── columns.tsx │ │ │ │ │ ├── request-distribution-chart.tsx │ │ │ │ │ ├── requests-table.tsx │ │ │ │ │ ├── show-requests.tsx │ │ │ │ │ └── status-request-filter.tsx │ │ │ │ ├── search-command.tsx │ │ │ │ ├── settings/ │ │ │ │ │ ├── ai-form.tsx │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── add-api-key.tsx │ │ │ │ │ │ └── show-api-keys.tsx │ │ │ │ │ ├── billing/ │ │ │ │ │ │ ├── show-billing-invoices.tsx │ │ │ │ │ │ ├── show-billing.tsx │ │ │ │ │ │ ├── show-invoices.tsx │ │ │ │ │ │ └── show-welcome-dokploy.tsx │ │ │ │ │ ├── certificates/ │ │ │ │ │ │ ├── add-certificate.tsx │ │ │ │ │ │ ├── show-certificates.tsx │ │ │ │ │ │ └── utils.ts │ │ │ │ │ ├── cluster/ │ │ │ │ │ │ ├── nodes/ │ │ │ │ │ │ │ ├── add-node.tsx │ │ │ │ │ │ │ ├── manager/ │ │ │ │ │ │ │ │ └── add-manager.tsx │ │ │ │ │ │ │ ├── show-node-data.tsx │ │ │ │ │ │ │ ├── show-nodes-modal.tsx │ │ │ │ │ │ │ ├── show-nodes.tsx │ │ │ │ │ │ │ └── workers/ │ │ │ │ │ │ │ └── add-worker.tsx │ │ │ │ │ │ └── registry/ │ │ │ │ │ │ ├── handle-registry.tsx │ │ │ │ │ │ └── show-registry.tsx │ │ │ │ │ ├── destination/ │ │ │ │ │ │ ├── constants.ts │ │ │ │ │ │ ├── handle-destinations.tsx │ │ │ │ │ │ └── show-destinations.tsx │ │ │ │ │ ├── git/ │ │ │ │ │ │ ├── bitbucket/ │ │ │ │ │ │ │ ├── add-bitbucket-provider.tsx │ │ │ │ │ │ │ └── edit-bitbucket-provider.tsx │ │ │ │ │ │ ├── gitea/ │ │ │ │ │ │ │ ├── add-gitea-provider.tsx │ │ │ │ │ │ │ └── edit-gitea-provider.tsx │ │ │ │ │ │ ├── github/ │ │ │ │ │ │ │ ├── add-github-provider.tsx │ │ │ │ │ │ │ └── edit-github-provider.tsx │ │ │ │ │ │ ├── gitlab/ │ │ │ │ │ │ │ ├── add-gitlab-provider.tsx │ │ │ │ │ │ │ └── edit-gitlab-provider.tsx │ │ │ │ │ │ └── show-git-providers.tsx │ │ │ │ │ ├── handle-ai.tsx │ │ │ │ │ ├── linking-account/ │ │ │ │ │ │ └── linking-account.tsx │ │ │ │ │ ├── notifications/ │ │ │ │ │ │ ├── handle-notifications.tsx │ │ │ │ │ │ └── show-notifications.tsx │ │ │ │ │ ├── profile/ │ │ │ │ │ │ ├── configure-2fa.tsx │ │ │ │ │ │ ├── enable-2fa.tsx │ │ │ │ │ │ └── profile-form.tsx │ │ │ │ │ ├── servers/ │ │ │ │ │ │ ├── actions/ │ │ │ │ │ │ │ ├── show-dokploy-actions.tsx │ │ │ │ │ │ │ ├── show-server-actions.tsx │ │ │ │ │ │ │ ├── show-storage-actions.tsx │ │ │ │ │ │ │ ├── show-traefik-actions.tsx │ │ │ │ │ │ │ └── toggle-docker-cleanup.tsx │ │ │ │ │ │ ├── edit-script.tsx │ │ │ │ │ │ ├── gpu-support-modal.tsx │ │ │ │ │ │ ├── gpu-support.tsx │ │ │ │ │ │ ├── handle-servers.tsx │ │ │ │ │ │ ├── security-audit.tsx │ │ │ │ │ │ ├── setup-monitoring.tsx │ │ │ │ │ │ ├── setup-server.tsx │ │ │ │ │ │ ├── show-docker-containers-modal.tsx │ │ │ │ │ │ ├── show-monitoring-modal.tsx │ │ │ │ │ │ ├── show-schedules-modal.tsx │ │ │ │ │ │ ├── show-servers.tsx │ │ │ │ │ │ ├── show-swarm-overview-modal.tsx │ │ │ │ │ │ ├── show-traefik-file-system-modal.tsx │ │ │ │ │ │ ├── validate-server.tsx │ │ │ │ │ │ └── welcome-stripe/ │ │ │ │ │ │ ├── create-server.tsx │ │ │ │ │ │ ├── create-ssh-key.tsx │ │ │ │ │ │ ├── setup.tsx │ │ │ │ │ │ ├── verify.tsx │ │ │ │ │ │ └── welcome-suscription.tsx │ │ │ │ │ ├── ssh-keys/ │ │ │ │ │ │ ├── handle-ssh-keys.tsx │ │ │ │ │ │ └── show-ssh-keys.tsx │ │ │ │ │ ├── tags/ │ │ │ │ │ │ ├── handle-tag.tsx │ │ │ │ │ │ └── tag-manager.tsx │ │ │ │ │ ├── users/ │ │ │ │ │ │ ├── add-invitation.tsx │ │ │ │ │ │ ├── add-permissions.tsx │ │ │ │ │ │ ├── change-role.tsx │ │ │ │ │ │ ├── show-invitations.tsx │ │ │ │ │ │ └── show-users.tsx │ │ │ │ │ ├── web-domain.tsx │ │ │ │ │ ├── web-server/ │ │ │ │ │ │ ├── docker-terminal-modal.tsx │ │ │ │ │ │ ├── edit-traefik-env.tsx │ │ │ │ │ │ ├── local-server-config.tsx │ │ │ │ │ │ ├── manage-traefik-ports.tsx │ │ │ │ │ │ ├── show-modal-logs.tsx │ │ │ │ │ │ ├── terminal-modal.tsx │ │ │ │ │ │ ├── terminal.tsx │ │ │ │ │ │ ├── toggle-auto-check-updates.tsx │ │ │ │ │ │ ├── update-server-ip.tsx │ │ │ │ │ │ ├── update-server.tsx │ │ │ │ │ │ └── update-webserver.tsx │ │ │ │ │ └── web-server.tsx │ │ │ │ ├── shared/ │ │ │ │ │ ├── rebuild-database.tsx │ │ │ │ │ └── show-database-advanced-settings.tsx │ │ │ │ └── swarm/ │ │ │ │ ├── applications/ │ │ │ │ │ ├── columns.tsx │ │ │ │ │ ├── data-table.tsx │ │ │ │ │ └── show-applications.tsx │ │ │ │ ├── details/ │ │ │ │ │ ├── details-card.tsx │ │ │ │ │ └── show-node-config.tsx │ │ │ │ └── monitoring-card.tsx │ │ │ ├── icons/ │ │ │ │ ├── data-tools-icons.tsx │ │ │ │ └── notification-icons.tsx │ │ │ ├── layouts/ │ │ │ │ ├── dashboard-layout.tsx │ │ │ │ ├── onboarding-layout.tsx │ │ │ │ ├── side.tsx │ │ │ │ ├── update-server.tsx │ │ │ │ └── user-nav.tsx │ │ │ ├── proprietary/ │ │ │ │ ├── audit-logs/ │ │ │ │ │ ├── columns.tsx │ │ │ │ │ ├── data-table.tsx │ │ │ │ │ └── show-audit-logs.tsx │ │ │ │ ├── auth/ │ │ │ │ │ ├── sign-in-with-github.tsx │ │ │ │ │ └── sign-in-with-google.tsx │ │ │ │ ├── enterprise-feature-gate.tsx │ │ │ │ ├── license-keys/ │ │ │ │ │ └── license-key.tsx │ │ │ │ ├── roles/ │ │ │ │ │ └── manage-custom-roles.tsx │ │ │ │ ├── sso/ │ │ │ │ │ ├── register-oidc-dialog.tsx │ │ │ │ │ ├── register-saml-dialog.tsx │ │ │ │ │ ├── sign-in-with-sso.tsx │ │ │ │ │ └── sso-settings.tsx │ │ │ │ └── whitelabeling/ │ │ │ │ ├── whitelabeling-preview.tsx │ │ │ │ ├── whitelabeling-provider.tsx │ │ │ │ └── whitelabeling-settings.tsx │ │ │ ├── shared/ │ │ │ │ ├── ChatwootWidget.tsx │ │ │ │ ├── HubSpotWidget.tsx │ │ │ │ ├── advance-breadcrumb.tsx │ │ │ │ ├── alert-block.tsx │ │ │ │ ├── breadcrumb-sidebar.tsx │ │ │ │ ├── code-editor.tsx │ │ │ │ ├── compose-spec.json │ │ │ │ ├── date-tooltip.tsx │ │ │ │ ├── dialog-action.tsx │ │ │ │ ├── drawer-logs.tsx │ │ │ │ ├── focus-shortcut-input.tsx │ │ │ │ ├── logo.tsx │ │ │ │ ├── status-tooltip.tsx │ │ │ │ ├── tag-badge.tsx │ │ │ │ ├── tag-filter.tsx │ │ │ │ ├── tag-selector.tsx │ │ │ │ └── toggle-visibility-input.tsx │ │ │ └── ui/ │ │ │ ├── accordion.tsx │ │ │ ├── alert-dialog.tsx │ │ │ ├── alert.tsx │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── breadcrumb.tsx │ │ │ ├── button.tsx │ │ │ ├── calendar.tsx │ │ │ ├── card.tsx │ │ │ ├── chart.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── command.tsx │ │ │ ├── dialog.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── dropzone.tsx │ │ │ ├── file-tree.tsx │ │ │ ├── form.tsx │ │ │ ├── input-otp.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── modeToggle.tsx │ │ │ ├── number-input.tsx │ │ │ ├── popover.tsx │ │ │ ├── progress.tsx │ │ │ ├── radio-group.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── secrets.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── sidebar.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── sonner.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ ├── textarea.tsx │ │ │ ├── time-badge.tsx │ │ │ ├── toggle.tsx │ │ │ └── tooltip.tsx │ │ ├── components.json │ │ ├── docker/ │ │ │ ├── build.sh │ │ │ ├── feat.sh │ │ │ └── push.sh │ │ ├── drizzle/ │ │ │ ├── 0000_reflective_puck.sql │ │ │ ├── 0001_striped_tattoo.sql │ │ │ ├── 0002_ambiguous_carlie_cooper.sql │ │ │ ├── 0003_square_lightspeed.sql │ │ │ ├── 0004_nice_tenebrous.sql │ │ │ ├── 0005_cute_terror.sql │ │ │ ├── 0006_oval_jimmy_woo.sql │ │ │ ├── 0007_cute_guardsmen.sql │ │ │ ├── 0008_lazy_sage.sql │ │ │ ├── 0009_majestic_spencer_smythe.sql │ │ │ ├── 0010_lean_black_widow.sql │ │ │ ├── 0011_petite_calypso.sql │ │ │ ├── 0012_chubby_umar.sql │ │ │ ├── 0013_blushing_starjammers.sql │ │ │ ├── 0014_same_hammerhead.sql │ │ │ ├── 0015_fearless_callisto.sql │ │ │ ├── 0016_chunky_leopardon.sql │ │ │ ├── 0017_minor_post.sql │ │ │ ├── 0018_careful_killmonger.sql │ │ │ ├── 0019_heavy_freak.sql │ │ │ ├── 0020_fantastic_slapstick.sql │ │ │ ├── 0021_premium_sebastian_shaw.sql │ │ │ ├── 0022_warm_colonel_america.sql │ │ │ ├── 0023_icy_maverick.sql │ │ │ ├── 0024_dapper_supernaut.sql │ │ │ ├── 0025_lying_mephisto.sql │ │ │ ├── 0026_known_dormammu.sql │ │ │ ├── 0027_red_lady_bullseye.sql │ │ │ ├── 0028_jittery_eternity.sql │ │ │ ├── 0029_colossal_zodiak.sql │ │ │ ├── 0030_little_kabuki.sql │ │ │ ├── 0031_steep_vulture.sql │ │ │ ├── 0032_flashy_shadow_king.sql │ │ │ ├── 0033_white_hawkeye.sql │ │ │ ├── 0034_aspiring_secret_warriors.sql │ │ │ ├── 0035_cool_gravity.sql │ │ │ ├── 0036_tired_ronan.sql │ │ │ ├── 0037_legal_namor.sql │ │ │ ├── 0038_rapid_landau.sql │ │ │ ├── 0039_many_tiger_shark.sql │ │ │ ├── 0040_graceful_wolfsbane.sql │ │ │ ├── 0041_huge_bruce_banner.sql │ │ │ ├── 0042_fancy_havok.sql │ │ │ ├── 0043_closed_naoko.sql │ │ │ ├── 0044_sour_true_believers.sql │ │ │ ├── 0045_smiling_blur.sql │ │ │ ├── 0046_purple_sleeper.sql │ │ │ ├── 0047_tidy_revanche.sql │ │ │ ├── 0048_flat_expediter.sql │ │ │ ├── 0049_dark_leopardon.sql │ │ │ ├── 0050_nappy_wrecker.sql │ │ │ ├── 0051_hard_gorgon.sql │ │ │ ├── 0052_bumpy_luckman.sql │ │ │ ├── 0053_broken_kulan_gath.sql │ │ │ ├── 0054_nervous_spencer_smythe.sql │ │ │ ├── 0055_next_serpent_society.sql │ │ │ ├── 0056_majestic_skaar.sql │ │ │ ├── 0057_tricky_living_tribunal.sql │ │ │ ├── 0058_brown_sharon_carter.sql │ │ │ ├── 0059_striped_bill_hollister.sql │ │ │ ├── 0060_disable-aggressive-cache.sql │ │ │ ├── 0061_many_molten_man.sql │ │ │ ├── 0062_slippery_white_tiger.sql │ │ │ ├── 0063_panoramic_dreadnoughts.sql │ │ │ ├── 0064_previous_agent_brand.sql │ │ │ ├── 0065_daily_zaladane.sql │ │ │ ├── 0066_yielding_echo.sql │ │ │ ├── 0067_condemned_sugar_man.sql │ │ │ ├── 0068_complex_rhino.sql │ │ │ ├── 0069_legal_bill_hollister.sql │ │ │ ├── 0070_useful_serpent_society.sql │ │ │ ├── 0071_flaky_black_queen.sql │ │ │ ├── 0072_green_susan_delgado.sql │ │ │ ├── 0073_hot_domino.sql │ │ │ ├── 0074_black_quasar.sql │ │ │ ├── 0075_young_typhoid_mary.sql │ │ │ ├── 0076_young_sharon_ventura.sql │ │ │ ├── 0077_chemical_dreadnoughts.sql │ │ │ ├── 0078_uneven_omega_sentinel.sql │ │ │ ├── 0079_bizarre_wendell_rand.sql │ │ │ ├── 0080_sleepy_sinister_six.sql │ │ │ ├── 0081_lovely_mentallo.sql │ │ │ ├── 0082_clean_mandarin.sql │ │ │ ├── 0083_parallel_stranger.sql │ │ │ ├── 0084_thin_iron_lad.sql │ │ │ ├── 0085_equal_captain_stacy.sql │ │ │ ├── 0086_rainy_gertrude_yorkes.sql │ │ │ ├── 0087_lively_risque.sql │ │ │ ├── 0088_illegal_ma_gnuci.sql │ │ │ ├── 0089_noisy_sandman.sql │ │ │ ├── 0090_clean_wolf_cub.sql │ │ │ ├── 0091_spotty_kulan_gath.sql │ │ │ ├── 0092_stiff_the_watchers.sql │ │ │ ├── 0093_nice_gorilla_man.sql │ │ │ ├── 0094_numerous_carmella_unuscione.sql │ │ │ ├── 0095_curly_justice.sql │ │ │ ├── 0096_small_shaman.sql │ │ │ ├── 0097_hard_lizard.sql │ │ │ ├── 0098_conscious_chat.sql │ │ │ ├── 0099_wise_golden_guardian.sql │ │ │ ├── 0100_purple_rogue.sql │ │ │ ├── 0101_moaning_blazing_skull.sql │ │ │ ├── 0102_opposite_grandmaster.sql │ │ │ ├── 0103_cultured_pestilence.sql │ │ │ ├── 0104_omniscient_randall.sql │ │ │ ├── 0105_clumsy_quicksilver.sql │ │ │ ├── 0106_purple_maggott.sql │ │ │ ├── 0107_loud_kang.sql │ │ │ ├── 0108_lazy_next_avengers.sql │ │ │ ├── 0109_remarkable_sauron.sql │ │ │ ├── 0110_red_psynapse.sql │ │ │ ├── 0111_mushy_wolfsbane.sql │ │ │ ├── 0112_freezing_skrulls.sql │ │ │ ├── 0113_complete_rafael_vega.sql │ │ │ ├── 0114_dry_black_tom.sql │ │ │ ├── 0115_serious_black_bird.sql │ │ │ ├── 0116_amusing_firedrake.sql │ │ │ ├── 0117_lumpy_nuke.sql │ │ │ ├── 0118_loose_anita_blake.sql │ │ │ ├── 0119_bouncy_morbius.sql │ │ │ ├── 0120_lame_captain_midlands.sql │ │ │ ├── 0121_rainy_cargill.sql │ │ │ ├── 0122_absent_frightful_four.sql │ │ │ ├── 0123_cloudy_piledriver.sql │ │ │ ├── 0124_certain_cloak.sql │ │ │ ├── 0125_neat_the_phantom.sql │ │ │ ├── 0126_nifty_monster_badoon.sql │ │ │ ├── 0127_superb_alice.sql │ │ │ ├── 0128_hard_falcon.sql │ │ │ ├── 0129_pale_roughhouse.sql │ │ │ ├── 0130_perpetual_screwball.sql │ │ │ ├── 0131_volatile_beast.sql │ │ │ ├── 0132_clean_layla_miller.sql │ │ │ ├── 0133_striped_the_order.sql │ │ │ ├── 0134_strong_hercules.sql │ │ │ ├── 0135_illegal_magik.sql │ │ │ ├── 0136_tidy_puff_adder.sql │ │ │ ├── 0137_colossal_sally_floyd.sql │ │ │ ├── 0138_pretty_ironclad.sql │ │ │ ├── 0139_brave_bloodstorm.sql │ │ │ ├── 0140_lame_mattie_franklin.sql │ │ │ ├── 0141_plain_earthquake.sql │ │ │ ├── 0142_outstanding_tusk.sql │ │ │ ├── 0143_brown_ultron.sql │ │ │ ├── 0144_odd_gunslinger.sql │ │ │ ├── 0145_remarkable_titania.sql │ │ │ ├── 0146_bumpy_morg.sql │ │ │ ├── 0147_right_lake.sql │ │ │ ├── 0148_futuristic_bullseye.sql │ │ │ ├── 0149_rare_radioactive_man.sql │ │ │ ├── 0150_nappy_blue_blade.sql │ │ │ ├── 0151_modern_sunfire.sql │ │ │ ├── 0152_odd_firelord.sql │ │ │ └── meta/ │ │ │ ├── 0000_snapshot.json │ │ │ ├── 0001_snapshot.json │ │ │ ├── 0002_snapshot.json │ │ │ ├── 0003_snapshot.json │ │ │ ├── 0004_snapshot.json │ │ │ ├── 0005_snapshot.json │ │ │ ├── 0006_snapshot.json │ │ │ ├── 0007_snapshot.json │ │ │ ├── 0008_snapshot.json │ │ │ ├── 0009_snapshot.json │ │ │ ├── 0010_snapshot.json │ │ │ ├── 0011_snapshot.json │ │ │ ├── 0012_snapshot.json │ │ │ ├── 0013_snapshot.json │ │ │ ├── 0014_snapshot.json │ │ │ ├── 0015_snapshot.json │ │ │ ├── 0016_snapshot.json │ │ │ ├── 0017_snapshot.json │ │ │ ├── 0018_snapshot.json │ │ │ ├── 0019_snapshot.json │ │ │ ├── 0020_snapshot.json │ │ │ ├── 0021_snapshot.json │ │ │ ├── 0022_snapshot.json │ │ │ ├── 0023_snapshot.json │ │ │ ├── 0024_snapshot.json │ │ │ ├── 0025_snapshot.json │ │ │ ├── 0026_snapshot.json │ │ │ ├── 0027_snapshot.json │ │ │ ├── 0028_snapshot.json │ │ │ ├── 0029_snapshot.json │ │ │ ├── 0030_snapshot.json │ │ │ ├── 0031_snapshot.json │ │ │ ├── 0032_snapshot.json │ │ │ ├── 0033_snapshot.json │ │ │ ├── 0034_snapshot.json │ │ │ ├── 0035_snapshot.json │ │ │ ├── 0036_snapshot.json │ │ │ ├── 0037_snapshot.json │ │ │ ├── 0038_snapshot.json │ │ │ ├── 0039_snapshot.json │ │ │ ├── 0040_snapshot.json │ │ │ ├── 0041_snapshot.json │ │ │ ├── 0042_snapshot.json │ │ │ ├── 0043_snapshot.json │ │ │ ├── 0044_snapshot.json │ │ │ ├── 0045_snapshot.json │ │ │ ├── 0046_snapshot.json │ │ │ ├── 0047_snapshot.json │ │ │ ├── 0048_snapshot.json │ │ │ ├── 0049_snapshot.json │ │ │ ├── 0050_snapshot.json │ │ │ ├── 0051_snapshot.json │ │ │ ├── 0052_snapshot.json │ │ │ ├── 0053_snapshot.json │ │ │ ├── 0054_snapshot.json │ │ │ ├── 0055_snapshot.json │ │ │ ├── 0056_snapshot.json │ │ │ ├── 0057_snapshot.json │ │ │ ├── 0058_snapshot.json │ │ │ ├── 0059_snapshot.json │ │ │ ├── 0060_snapshot.json │ │ │ ├── 0061_snapshot.json │ │ │ ├── 0062_snapshot.json │ │ │ ├── 0063_snapshot.json │ │ │ ├── 0064_snapshot.json │ │ │ ├── 0065_snapshot.json │ │ │ ├── 0066_snapshot.json │ │ │ ├── 0067_snapshot.json │ │ │ ├── 0068_snapshot.json │ │ │ ├── 0069_snapshot.json │ │ │ ├── 0070_snapshot.json │ │ │ ├── 0071_snapshot.json │ │ │ ├── 0072_snapshot.json │ │ │ ├── 0073_snapshot.json │ │ │ ├── 0074_snapshot.json │ │ │ ├── 0075_snapshot.json │ │ │ ├── 0076_snapshot.json │ │ │ ├── 0077_snapshot.json │ │ │ ├── 0078_snapshot.json │ │ │ ├── 0079_snapshot.json │ │ │ ├── 0080_snapshot.json │ │ │ ├── 0081_snapshot.json │ │ │ ├── 0082_snapshot.json │ │ │ ├── 0083_snapshot.json │ │ │ ├── 0084_snapshot.json │ │ │ ├── 0085_snapshot.json │ │ │ ├── 0086_snapshot.json │ │ │ ├── 0087_snapshot.json │ │ │ ├── 0088_snapshot.json │ │ │ ├── 0089_snapshot.json │ │ │ ├── 0090_snapshot.json │ │ │ ├── 0091_snapshot.json │ │ │ ├── 0092_snapshot.json │ │ │ ├── 0093_snapshot.json │ │ │ ├── 0094_snapshot.json │ │ │ ├── 0095_snapshot.json │ │ │ ├── 0096_snapshot.json │ │ │ ├── 0097_snapshot.json │ │ │ ├── 0098_snapshot.json │ │ │ ├── 0099_snapshot.json │ │ │ ├── 0100_snapshot.json │ │ │ ├── 0101_snapshot.json │ │ │ ├── 0102_snapshot.json │ │ │ ├── 0103_snapshot.json │ │ │ ├── 0104_snapshot.json │ │ │ ├── 0105_snapshot.json │ │ │ ├── 0106_snapshot.json │ │ │ ├── 0107_snapshot.json │ │ │ ├── 0108_snapshot.json │ │ │ ├── 0109_snapshot.json │ │ │ ├── 0110_snapshot.json │ │ │ ├── 0111_snapshot.json │ │ │ ├── 0112_snapshot.json │ │ │ ├── 0113_snapshot.json │ │ │ ├── 0114_snapshot.json │ │ │ ├── 0115_snapshot.json │ │ │ ├── 0116_snapshot.json │ │ │ ├── 0117_snapshot.json │ │ │ ├── 0118_snapshot.json │ │ │ ├── 0119_snapshot.json │ │ │ ├── 0120_snapshot.json │ │ │ ├── 0121_snapshot.json │ │ │ ├── 0122_snapshot.json │ │ │ ├── 0123_snapshot.json │ │ │ ├── 0124_snapshot.json │ │ │ ├── 0125_snapshot.json │ │ │ ├── 0126_snapshot.json │ │ │ ├── 0127_snapshot.json │ │ │ ├── 0128_snapshot.json │ │ │ ├── 0129_snapshot.json │ │ │ ├── 0130_snapshot.json │ │ │ ├── 0131_snapshot.json │ │ │ ├── 0132_snapshot.json │ │ │ ├── 0133_snapshot.json │ │ │ ├── 0134_snapshot.json │ │ │ ├── 0135_snapshot.json │ │ │ ├── 0136_snapshot.json │ │ │ ├── 0137_snapshot.json │ │ │ ├── 0138_snapshot.json │ │ │ ├── 0139_snapshot.json │ │ │ ├── 0140_snapshot.json │ │ │ ├── 0141_snapshot.json │ │ │ ├── 0142_snapshot.json │ │ │ ├── 0143_snapshot.json │ │ │ ├── 0144_snapshot.json │ │ │ ├── 0145_snapshot.json │ │ │ ├── 0146_snapshot.json │ │ │ ├── 0147_snapshot.json │ │ │ ├── 0148_snapshot.json │ │ │ ├── 0149_snapshot.json │ │ │ ├── 0150_snapshot.json │ │ │ ├── 0151_snapshot.json │ │ │ ├── 0152_snapshot.json │ │ │ ├── _journal.json │ │ │ └── _journal.json.backup │ │ ├── esbuild.config.ts │ │ ├── hooks/ │ │ │ ├── use-health-check-after-mutation.ts │ │ │ ├── use-keyboard-nav.tsx │ │ │ ├── use-mobile.tsx │ │ │ └── useLocalStorage.tsx │ │ ├── lib/ │ │ │ ├── auth-client.ts │ │ │ ├── avatar-utils.ts │ │ │ ├── password-utils.ts │ │ │ ├── slug.ts │ │ │ └── utils.ts │ │ ├── migration.ts │ │ ├── next.config.mjs │ │ ├── package.json │ │ ├── pages/ │ │ │ ├── _app.tsx │ │ │ ├── _document.tsx │ │ │ ├── _error.tsx │ │ │ ├── accept-invitation/ │ │ │ │ └── [accept-invitation].tsx │ │ │ ├── api/ │ │ │ │ ├── [...trpc].ts │ │ │ │ ├── auth/ │ │ │ │ │ └── [...all].ts │ │ │ │ ├── deploy/ │ │ │ │ │ ├── [refreshToken].ts │ │ │ │ │ ├── compose/ │ │ │ │ │ │ └── [refreshToken].ts │ │ │ │ │ └── github.ts │ │ │ │ ├── health.ts │ │ │ │ ├── providers/ │ │ │ │ │ ├── gitea/ │ │ │ │ │ │ ├── authorize.ts │ │ │ │ │ │ ├── callback.ts │ │ │ │ │ │ └── helper.ts │ │ │ │ │ ├── github/ │ │ │ │ │ │ ├── setup.ts │ │ │ │ │ │ └── webhook.ts │ │ │ │ │ └── gitlab/ │ │ │ │ │ └── callback.ts │ │ │ │ ├── stripe/ │ │ │ │ │ └── webhook.ts │ │ │ │ └── trpc/ │ │ │ │ └── [trpc].ts │ │ │ ├── dashboard/ │ │ │ │ ├── deployments.tsx │ │ │ │ ├── docker.tsx │ │ │ │ ├── monitoring.tsx │ │ │ │ ├── project/ │ │ │ │ │ └── [projectId]/ │ │ │ │ │ └── environment/ │ │ │ │ │ ├── [environmentId]/ │ │ │ │ │ │ └── services/ │ │ │ │ │ │ ├── application/ │ │ │ │ │ │ │ └── [applicationId].tsx │ │ │ │ │ │ ├── compose/ │ │ │ │ │ │ │ └── [composeId].tsx │ │ │ │ │ │ ├── mariadb/ │ │ │ │ │ │ │ └── [mariadbId].tsx │ │ │ │ │ │ ├── mongo/ │ │ │ │ │ │ │ └── [mongoId].tsx │ │ │ │ │ │ ├── mysql/ │ │ │ │ │ │ │ └── [mysqlId].tsx │ │ │ │ │ │ ├── postgres/ │ │ │ │ │ │ │ └── [postgresId].tsx │ │ │ │ │ │ └── redis/ │ │ │ │ │ │ └── [redisId].tsx │ │ │ │ │ └── [environmentId].tsx │ │ │ │ ├── projects.tsx │ │ │ │ ├── requests.tsx │ │ │ │ ├── schedules.tsx │ │ │ │ ├── settings/ │ │ │ │ │ ├── ai.tsx │ │ │ │ │ ├── audit-logs.tsx │ │ │ │ │ ├── billing.tsx │ │ │ │ │ ├── certificates.tsx │ │ │ │ │ ├── cluster.tsx │ │ │ │ │ ├── destinations.tsx │ │ │ │ │ ├── git-providers.tsx │ │ │ │ │ ├── invoices.tsx │ │ │ │ │ ├── license.tsx │ │ │ │ │ ├── notifications.tsx │ │ │ │ │ ├── profile.tsx │ │ │ │ │ ├── registry.tsx │ │ │ │ │ ├── server.tsx │ │ │ │ │ ├── servers.tsx │ │ │ │ │ ├── ssh-keys.tsx │ │ │ │ │ ├── sso.tsx │ │ │ │ │ ├── tags.tsx │ │ │ │ │ ├── users.tsx │ │ │ │ │ └── whitelabeling.tsx │ │ │ │ ├── swarm.tsx │ │ │ │ └── traefik.tsx │ │ │ ├── index.tsx │ │ │ ├── invitation.tsx │ │ │ ├── register.tsx │ │ │ ├── reset-password.tsx │ │ │ ├── send-reset-password.tsx │ │ │ └── swagger.tsx │ │ ├── postcss.config.cjs │ │ ├── public/ │ │ │ ├── locales/ │ │ │ │ ├── az/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── de/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── en/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── es/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── fa/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── fr/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── id/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── it/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── ja/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── ko/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── kz/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── ml/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── nl/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── no/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── pl/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── pt-br/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── ru/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── tr/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── uk/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ ├── zh-Hans/ │ │ │ │ │ ├── common.json │ │ │ │ │ └── settings.json │ │ │ │ └── zh-Hant/ │ │ │ │ ├── common.json │ │ │ │ └── settings.json │ │ │ └── robots.txt │ │ ├── reset-2fa.ts │ │ ├── reset-password.ts │ │ ├── scripts/ │ │ │ └── generate-openapi.ts │ │ ├── server/ │ │ │ ├── api/ │ │ │ │ ├── root.ts │ │ │ │ ├── routers/ │ │ │ │ │ ├── admin.ts │ │ │ │ │ ├── ai.ts │ │ │ │ │ ├── application.ts │ │ │ │ │ ├── backup.ts │ │ │ │ │ ├── bitbucket.ts │ │ │ │ │ ├── certificate.ts │ │ │ │ │ ├── cluster.ts │ │ │ │ │ ├── compose.ts │ │ │ │ │ ├── deployment.ts │ │ │ │ │ ├── destination.ts │ │ │ │ │ ├── docker.ts │ │ │ │ │ ├── domain.ts │ │ │ │ │ ├── environment.ts │ │ │ │ │ ├── git-provider.ts │ │ │ │ │ ├── gitea.ts │ │ │ │ │ ├── github.ts │ │ │ │ │ ├── gitlab.ts │ │ │ │ │ ├── mariadb.ts │ │ │ │ │ ├── mongo.ts │ │ │ │ │ ├── mount.ts │ │ │ │ │ ├── mysql.ts │ │ │ │ │ ├── notification.ts │ │ │ │ │ ├── organization.ts │ │ │ │ │ ├── patch.ts │ │ │ │ │ ├── port.ts │ │ │ │ │ ├── postgres.ts │ │ │ │ │ ├── preview-deployment.ts │ │ │ │ │ ├── project.ts │ │ │ │ │ ├── proprietary/ │ │ │ │ │ │ ├── audit-log.ts │ │ │ │ │ │ ├── custom-role.ts │ │ │ │ │ │ ├── license-key.ts │ │ │ │ │ │ ├── sso.ts │ │ │ │ │ │ └── whitelabeling.ts │ │ │ │ │ ├── redirects.ts │ │ │ │ │ ├── redis.ts │ │ │ │ │ ├── registry.ts │ │ │ │ │ ├── rollbacks.ts │ │ │ │ │ ├── schedule.ts │ │ │ │ │ ├── security.ts │ │ │ │ │ ├── server.ts │ │ │ │ │ ├── settings.ts │ │ │ │ │ ├── ssh-key.ts │ │ │ │ │ ├── stripe.ts │ │ │ │ │ ├── swarm.ts │ │ │ │ │ ├── tag.ts │ │ │ │ │ ├── user.ts │ │ │ │ │ └── volume-backups.ts │ │ │ │ ├── trpc.ts │ │ │ │ └── utils/ │ │ │ │ └── audit.ts │ │ │ ├── db/ │ │ │ │ ├── drizzle.config.ts │ │ │ │ ├── index.ts │ │ │ │ ├── migration.ts │ │ │ │ ├── reset.ts │ │ │ │ ├── schema/ │ │ │ │ │ └── index.ts │ │ │ │ └── validations/ │ │ │ │ ├── domain.ts │ │ │ │ └── index.ts │ │ │ ├── queues/ │ │ │ │ ├── deployments-queue.ts │ │ │ │ ├── queue-types.ts │ │ │ │ ├── queueSetup.ts │ │ │ │ └── redis-connection.ts │ │ │ ├── server.ts │ │ │ ├── utils/ │ │ │ │ ├── backup.ts │ │ │ │ ├── deploy.ts │ │ │ │ ├── docker.ts │ │ │ │ ├── enterprise.ts │ │ │ │ └── stripe.ts │ │ │ └── wss/ │ │ │ ├── docker-container-logs.ts │ │ │ ├── docker-container-terminal.ts │ │ │ ├── docker-stats.ts │ │ │ ├── drawer-logs.ts │ │ │ ├── listen-deployment.ts │ │ │ ├── terminal.ts │ │ │ └── utils.ts │ │ ├── setup.ts │ │ ├── styles/ │ │ │ └── globals.css │ │ ├── tailwind.config.ts │ │ ├── templates/ │ │ │ ├── templates.ts │ │ │ └── utils/ │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.server.json │ │ ├── types/ │ │ │ └── chatwoot.d.ts │ │ ├── utils/ │ │ │ ├── api.ts │ │ │ ├── gitea-utils.ts │ │ │ ├── hooks/ │ │ │ │ ├── use-debounce.ts │ │ │ │ ├── use-url.ts │ │ │ │ └── use-whitelabeling.ts │ │ │ └── schema.ts │ │ └── wait-for-postgres.ts │ ├── monitoring/ │ │ ├── .gitignore │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── config/ │ │ │ └── metrics.go │ │ ├── containers/ │ │ │ ├── config.go │ │ │ ├── monitor.go │ │ │ └── types.go │ │ ├── database/ │ │ │ ├── cleanup.go │ │ │ ├── containers.go │ │ │ ├── db.go │ │ │ └── server.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ ├── middleware/ │ │ │ └── auth.go │ │ └── monitoring/ │ │ └── monitor.go │ └── schedules/ │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── src/ │ │ ├── index.ts │ │ ├── logger.ts │ │ ├── queue.ts │ │ ├── schema.ts │ │ ├── utils.ts │ │ └── workers.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── biome.json ├── package.json ├── packages/ │ └── server/ │ ├── auth-schema.ts │ ├── auth-schema2.ts │ ├── esbuild.config.ts │ ├── package.json │ ├── scripts/ │ │ ├── switchToDist.js │ │ └── switchToSrc.js │ ├── src/ │ │ ├── auth/ │ │ │ └── random-password.ts │ │ ├── constants/ │ │ │ └── index.ts │ │ ├── db/ │ │ │ ├── constants.ts │ │ │ ├── index.ts │ │ │ ├── schema/ │ │ │ │ ├── account.ts │ │ │ │ ├── ai.ts │ │ │ │ ├── application.ts │ │ │ │ ├── audit-log.ts │ │ │ │ ├── backups.ts │ │ │ │ ├── bitbucket.ts │ │ │ │ ├── certificate.ts │ │ │ │ ├── compose.ts │ │ │ │ ├── dbml.ts │ │ │ │ ├── deployment.ts │ │ │ │ ├── destination.ts │ │ │ │ ├── domain.ts │ │ │ │ ├── environment.ts │ │ │ │ ├── git-provider.ts │ │ │ │ ├── gitea.ts │ │ │ │ ├── github.ts │ │ │ │ ├── gitlab.ts │ │ │ │ ├── index.ts │ │ │ │ ├── mariadb.ts │ │ │ │ ├── mongo.ts │ │ │ │ ├── mount.ts │ │ │ │ ├── mysql.ts │ │ │ │ ├── notification.ts │ │ │ │ ├── patch.ts │ │ │ │ ├── port.ts │ │ │ │ ├── postgres.ts │ │ │ │ ├── preview-deployments.ts │ │ │ │ ├── project.ts │ │ │ │ ├── redirects.ts │ │ │ │ ├── redis.ts │ │ │ │ ├── registry.ts │ │ │ │ ├── rollbacks.ts │ │ │ │ ├── schedule.ts │ │ │ │ ├── schema.dbml │ │ │ │ ├── security.ts │ │ │ │ ├── server.ts │ │ │ │ ├── session.ts │ │ │ │ ├── shared.ts │ │ │ │ ├── ssh-key.ts │ │ │ │ ├── sso.ts │ │ │ │ ├── tag.ts │ │ │ │ ├── user.ts │ │ │ │ ├── utils.ts │ │ │ │ ├── volume-backups.ts │ │ │ │ └── web-server-settings.ts │ │ │ └── validations/ │ │ │ ├── domain.ts │ │ │ └── index.ts │ │ ├── emails/ │ │ │ ├── .gitignore │ │ │ ├── emails/ │ │ │ │ ├── build-failed.tsx │ │ │ │ ├── build-success.tsx │ │ │ │ ├── database-backup.tsx │ │ │ │ ├── docker-cleanup.tsx │ │ │ │ ├── dokploy-restart.tsx │ │ │ │ ├── invitation.tsx │ │ │ │ ├── notion-magic-link.tsx │ │ │ │ ├── plaid-verify-identity.tsx │ │ │ │ ├── stripe-welcome.tsx │ │ │ │ ├── vercel-invite-user.tsx │ │ │ │ └── volume-backup.tsx │ │ │ ├── package.json │ │ │ └── readme.md │ │ ├── index.ts │ │ ├── lib/ │ │ │ ├── access-control.ts │ │ │ ├── auth.ts │ │ │ └── logger.ts │ │ ├── monitoring/ │ │ │ └── utils.ts │ │ ├── services/ │ │ │ ├── admin.ts │ │ │ ├── ai.ts │ │ │ ├── application.ts │ │ │ ├── backup.ts │ │ │ ├── bitbucket.ts │ │ │ ├── cdn.ts │ │ │ ├── certificate.ts │ │ │ ├── cluster.ts │ │ │ ├── compose.ts │ │ │ ├── deployment.ts │ │ │ ├── destination.ts │ │ │ ├── docker.ts │ │ │ ├── domain.ts │ │ │ ├── environment.ts │ │ │ ├── git-provider.ts │ │ │ ├── gitea.ts │ │ │ ├── github.ts │ │ │ ├── gitlab.ts │ │ │ ├── mariadb.ts │ │ │ ├── mongo.ts │ │ │ ├── mount.ts │ │ │ ├── mysql.ts │ │ │ ├── notification.ts │ │ │ ├── patch-repo.ts │ │ │ ├── patch.ts │ │ │ ├── permission.ts │ │ │ ├── port.ts │ │ │ ├── postgres.ts │ │ │ ├── preview-deployment.ts │ │ │ ├── project.ts │ │ │ ├── proprietary/ │ │ │ │ ├── audit-log.ts │ │ │ │ ├── license-key.ts │ │ │ │ └── sso.ts │ │ │ ├── redirect.ts │ │ │ ├── redis.ts │ │ │ ├── registry.ts │ │ │ ├── rollbacks.ts │ │ │ ├── schedule.ts │ │ │ ├── security.ts │ │ │ ├── server.ts │ │ │ ├── settings.ts │ │ │ ├── ssh-key.ts │ │ │ ├── user.ts │ │ │ ├── volume-backups.ts │ │ │ └── web-server-settings.ts │ │ ├── setup/ │ │ │ ├── config-paths.ts │ │ │ ├── monitoring-setup.ts │ │ │ ├── postgres-setup.ts │ │ │ ├── redis-setup.ts │ │ │ ├── server-audit.ts │ │ │ ├── server-setup.ts │ │ │ ├── server-validate.ts │ │ │ ├── setup.ts │ │ │ └── traefik-setup.ts │ │ ├── templates/ │ │ │ ├── github.ts │ │ │ ├── index.ts │ │ │ └── processors.ts │ │ ├── types/ │ │ │ ├── template.ts │ │ │ └── with.ts │ │ ├── utils/ │ │ │ ├── access-log/ │ │ │ │ ├── handler.ts │ │ │ │ ├── types.ts │ │ │ │ └── utils.ts │ │ │ ├── ai/ │ │ │ │ ├── index.ts │ │ │ │ └── select-ai-provider.ts │ │ │ ├── backups/ │ │ │ │ ├── compose.ts │ │ │ │ ├── index.ts │ │ │ │ ├── mariadb.ts │ │ │ │ ├── mongo.ts │ │ │ │ ├── mysql.ts │ │ │ │ ├── postgres.ts │ │ │ │ ├── utils.ts │ │ │ │ └── web-server.ts │ │ │ ├── builders/ │ │ │ │ ├── compose.ts │ │ │ │ ├── docker-file.ts │ │ │ │ ├── drop.ts │ │ │ │ ├── heroku.ts │ │ │ │ ├── index.ts │ │ │ │ ├── nixpacks.ts │ │ │ │ ├── paketo.ts │ │ │ │ ├── railpack.ts │ │ │ │ ├── static.ts │ │ │ │ └── utils.ts │ │ │ ├── cluster/ │ │ │ │ └── upload.ts │ │ │ ├── crons/ │ │ │ │ └── enterprise.ts │ │ │ ├── databases/ │ │ │ │ ├── mariadb.ts │ │ │ │ ├── mongo.ts │ │ │ │ ├── mysql.ts │ │ │ │ ├── postgres.ts │ │ │ │ ├── rebuild.ts │ │ │ │ └── redis.ts │ │ │ ├── docker/ │ │ │ │ ├── collision/ │ │ │ │ │ └── root-network.ts │ │ │ │ ├── collision.ts │ │ │ │ ├── compose/ │ │ │ │ │ ├── configs.ts │ │ │ │ │ ├── network.ts │ │ │ │ │ ├── secrets.ts │ │ │ │ │ ├── service.ts │ │ │ │ │ └── volume.ts │ │ │ │ ├── compose.ts │ │ │ │ ├── domain.ts │ │ │ │ ├── types.ts │ │ │ │ └── utils.ts │ │ │ ├── filesystem/ │ │ │ │ ├── directory.ts │ │ │ │ └── ssh.ts │ │ │ ├── gpu-setup.ts │ │ │ ├── notifications/ │ │ │ │ ├── build-error.ts │ │ │ │ ├── build-success.ts │ │ │ │ ├── database-backup.ts │ │ │ │ ├── docker-cleanup.ts │ │ │ │ ├── dokploy-restart.ts │ │ │ │ ├── server-threshold.ts │ │ │ │ ├── utils.ts │ │ │ │ └── volume-backup.ts │ │ │ ├── process/ │ │ │ │ ├── ExecError.ts │ │ │ │ ├── execAsync.ts │ │ │ │ └── spawnAsync.ts │ │ │ ├── providers/ │ │ │ │ ├── bitbucket.ts │ │ │ │ ├── docker.ts │ │ │ │ ├── git.ts │ │ │ │ ├── gitea.ts │ │ │ │ ├── github.ts │ │ │ │ ├── gitlab.ts │ │ │ │ └── raw.ts │ │ │ ├── restore/ │ │ │ │ ├── compose.ts │ │ │ │ ├── index.ts │ │ │ │ ├── mariadb.ts │ │ │ │ ├── mongo.ts │ │ │ │ ├── mysql.ts │ │ │ │ ├── postgres.ts │ │ │ │ ├── utils.ts │ │ │ │ └── web-server.ts │ │ │ ├── schedules/ │ │ │ │ ├── index.ts │ │ │ │ └── utils.ts │ │ │ ├── servers/ │ │ │ │ └── remote-docker.ts │ │ │ ├── startup/ │ │ │ │ └── cancell-deployments.ts │ │ │ ├── tracking/ │ │ │ │ └── hubspot.ts │ │ │ ├── traefik/ │ │ │ │ ├── application.ts │ │ │ │ ├── domain.ts │ │ │ │ ├── file-types.ts │ │ │ │ ├── middleware.ts │ │ │ │ ├── redirect.ts │ │ │ │ ├── security.ts │ │ │ │ ├── types.ts │ │ │ │ └── web-server.ts │ │ │ ├── volume-backups/ │ │ │ │ ├── backup.ts │ │ │ │ ├── index.ts │ │ │ │ ├── restore.ts │ │ │ │ └── utils.ts │ │ │ └── watch-paths/ │ │ │ └── should-deploy.ts │ │ ├── verification/ │ │ │ └── send-verification-email.tsx │ │ └── wss/ │ │ └── utils.ts │ ├── tsconfig.json │ ├── tsconfig.server.json │ └── tsconfig.server.no-decl.json └── pnpm-workspace.yaml
SYMBOL INDEX (654 symbols across 318 files)
FILE: apps/api/src/schema.ts
type DeployJob (line 34) | type DeployJob = z.infer<typeof deployJobSchema>;
type CancelDeploymentJob (line 47) | type CancelDeploymentJob = z.infer<typeof cancelDeploymentSchema>;
FILE: apps/api/src/service.ts
constant DEFAULT_MAX_EVENTS (line 6) | const DEFAULT_MAX_EVENTS = 500;
constant MAX_EVENTS (line 7) | const MAX_EVENTS = DEFAULT_MAX_EVENTS;
type InngestEventRow (line 10) | type InngestEventRow = {
type InngestRun (line 32) | type InngestRun = {
function getEventReceivedAt (line 43) | function getEventReceivedAt(ev: InngestEventRow): string | undefined {
function runStatusToState (line 48) | function runStatusToState(
type DeploymentJobRow (line 130) | type DeploymentJobRow = {
function buildDeploymentRowsFromRuns (line 142) | function buildDeploymentRowsFromRuns(
FILE: apps/dokploy/__test__/deploy/application.real.test.ts
constant REAL_TEST_TIMEOUT (line 9) | const REAL_TEST_TIMEOUT = 180000;
function cleanupDocker (line 146) | async function cleanupDocker(appName: string) {
function cleanupFiles (line 156) | async function cleanupFiles(appName: string) {
FILE: apps/dokploy/__test__/drop/drop.test.ts
constant OUTPUT_BASE (line 9) | const OUTPUT_BASE = "./__test__/drop/zips/output";
FILE: apps/dokploy/__test__/env/environment-access-fallback.test.ts
type Environment (line 4) | type Environment = {
type Project (line 10) | type Project = {
function selectAccessibleEnvironment (line 20) | function selectAccessibleEnvironment(
FILE: apps/dokploy/__test__/permissions/enterprise-only-resources.test.ts
constant FREE_TIER_RESOURCES (line 7) | const FREE_TIER_RESOURCES = [
constant ENTERPRISE_RESOURCES (line 23) | const ENTERPRISE_RESOURCES = [
FILE: apps/dokploy/__test__/server/mechanizeDockerContainer.test.ts
type MockCreateServiceOptions (line 5) | type MockCreateServiceOptions = {
FILE: apps/dokploy/__test__/templates/helpers.template.test.ts
type JWTParts (line 12) | type JWTParts = [string, string, string];
FILE: apps/dokploy/__test__/traefik/server/update-server-config.test.ts
type WebServerSettings (line 17) | type WebServerSettings = typeof webServerSettings.$inferSelect;
FILE: apps/dokploy/__test__/wss/readValidDirectory.test.ts
constant BASE (line 4) | const BASE = "/base";
FILE: apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx
type MenuItem (line 33) | type MenuItem = {
type Props (line 120) | interface Props {
FILE: apps/dokploy/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx
type Props (line 38) | interface Props {
type AddCommand (line 48) | type AddCommand = z.infer<typeof AddRedirectchema>;
FILE: apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/endpoint-spec-form.tsx
type EndpointSpecFormProps (line 29) | interface EndpointSpecFormProps {
FILE: apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/health-check-form.tsx
type HealthCheckFormProps (line 27) | interface HealthCheckFormProps {
FILE: apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/labels-form.tsx
type LabelsFormProps (line 30) | interface LabelsFormProps {
FILE: apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/mode-form.tsx
type ModeFormProps (line 24) | interface ModeFormProps {
FILE: apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/network-form.tsx
type NetworkFormProps (line 36) | interface NetworkFormProps {
FILE: apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/placement-form.tsx
type PlacementFormProps (line 35) | interface PlacementFormProps {
FILE: apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/restart-policy-form.tsx
type RestartPolicyFormProps (line 33) | interface RestartPolicyFormProps {
FILE: apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/rollback-config-form.tsx
type RollbackConfigFormProps (line 35) | interface RollbackConfigFormProps {
FILE: apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/stop-grace-period-form.tsx
type StopGracePeriodFormProps (line 24) | interface StopGracePeriodFormProps {
FILE: apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/update-config-form.tsx
type UpdateConfigFormProps (line 35) | interface UpdateConfigFormProps {
FILE: apps/dokploy/components/dashboard/application/advanced/general/add-command.tsx
type Props (line 26) | interface Props {
type AddCommand (line 41) | type AddCommand = z.infer<typeof AddRedirectSchema>;
FILE: apps/dokploy/components/dashboard/application/advanced/import/show-import.tsx
type ImportType (line 41) | type ImportType = z.infer<typeof ImportSchema>;
type Props (line 43) | interface Props {
FILE: apps/dokploy/components/dashboard/application/advanced/ports/handle-ports.tsx
type AddPort (line 43) | type AddPort = z.infer<typeof AddPortSchema>;
type Props (line 45) | interface Props {
FILE: apps/dokploy/components/dashboard/application/advanced/ports/show-port.tsx
type Props (line 16) | interface Props {
FILE: apps/dokploy/components/dashboard/application/advanced/redirects/handle-redirect.tsx
type AddRedirect (line 46) | type AddRedirect = z.infer<typeof AddRedirectchema>;
type Props (line 78) | interface Props {
FILE: apps/dokploy/components/dashboard/application/advanced/redirects/show-redirects.tsx
type Props (line 15) | interface Props {
FILE: apps/dokploy/components/dashboard/application/advanced/security/handle-security.tsx
type AddSecurity (line 34) | type AddSecurity = z.infer<typeof AddSecuritychema>;
type Props (line 36) | interface Props {
FILE: apps/dokploy/components/dashboard/application/advanced/security/show-security.tsx
type Props (line 18) | interface Props {
FILE: apps/dokploy/components/dashboard/application/advanced/show-build-server.tsx
type Props (line 37) | interface Props {
type Schema (line 67) | type Schema = z.infer<typeof schema>;
FILE: apps/dokploy/components/dashboard/application/advanced/show-resources.tsx
constant CPU_STEP (line 44) | const CPU_STEP = 0.25;
constant MEMORY_STEP_MB (line 45) | const MEMORY_STEP_MB = 256;
constant ULIMIT_PRESETS (line 75) | const ULIMIT_PRESETS = [
type ServiceType (line 91) | type ServiceType =
type Props (line 99) | interface Props {
type AddResources (line 104) | type AddResources = z.infer<typeof addResourcesSchema>;
FILE: apps/dokploy/components/dashboard/application/advanced/traefik/show-traefik-config.tsx
type Props (line 13) | interface Props {
FILE: apps/dokploy/components/dashboard/application/advanced/traefik/update-traefik-config.tsx
type UpdateTraefikConfig (line 35) | type UpdateTraefikConfig = z.infer<typeof UpdateTraefikConfigSchema>;
type Props (line 37) | interface Props {
FILE: apps/dokploy/components/dashboard/application/advanced/volumes/add-volumes.tsx
type Props (line 33) | interface Props {
type AddMount (line 80) | type AddMount = z.infer<typeof mySchema>;
FILE: apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx
type Props (line 18) | interface Props {
FILE: apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx
type UpdateMount (line 62) | type UpdateMount = z.infer<typeof mySchema>;
type Props (line 64) | interface Props {
FILE: apps/dokploy/components/dashboard/application/build/show.tsx
constant RAILPACK_VERSIONS (line 33) | const RAILPACK_VERSIONS = [
type BuildType (line 56) | enum BuildType {
type AddTemplate (line 102) | type AddTemplate = z.infer<typeof mySchema>;
type Props (line 104) | interface Props {
type ApplicationData (line 108) | interface ApplicationData {
function isValidBuildType (line 119) | function isValidBuildType(value: string): value is BuildType {
FILE: apps/dokploy/components/dashboard/application/deployments/cancel-queues.tsx
type Props (line 17) | interface Props {
FILE: apps/dokploy/components/dashboard/application/deployments/clear-deployments.tsx
type Props (line 17) | interface Props {
FILE: apps/dokploy/components/dashboard/application/deployments/kill-build.tsx
type Props (line 17) | interface Props {
FILE: apps/dokploy/components/dashboard/application/deployments/refresh-token.tsx
type Props (line 16) | interface Props {
FILE: apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx
type Props (line 17) | interface Props {
FILE: apps/dokploy/components/dashboard/application/deployments/show-deployments-modal.tsx
type Props (line 8) | interface Props {
FILE: apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx
type Props (line 36) | interface Props {
FILE: apps/dokploy/components/dashboard/application/domains/dns-helper-modal.tsx
type Props (line 14) | interface Props {
FILE: apps/dokploy/components/dashboard/application/domains/handle-domain.tsx
type CacheType (line 45) | type CacheType = "fetch" | "cache";
type Domain (line 119) | type Domain = z.infer<typeof domain>;
type Props (line 121) | interface Props {
FILE: apps/dokploy/components/dashboard/application/domains/show-domains.tsx
type ValidationState (line 36) | type ValidationState = {
type ValidationStates (line 45) | type ValidationStates = Record<string, ValidationState>;
type Props (line 47) | interface Props {
FILE: apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx
type EnvironmentSchema (line 31) | type EnvironmentSchema = z.infer<typeof addEnvironmentSchema>;
type Props (line 33) | interface Props {
FILE: apps/dokploy/components/dashboard/application/environment/show.tsx
type EnvironmentSchema (line 27) | type EnvironmentSchema = z.infer<typeof addEnvironmentSchema>;
type Props (line 29) | interface Props {
FILE: apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx
type BitbucketProvider (line 66) | type BitbucketProvider = z.infer<typeof BitbucketProviderSchema>;
type Props (line 68) | interface Props {
FILE: apps/dokploy/components/dashboard/application/general/generic/save-docker-provider.tsx
type DockerProvider (line 27) | type DockerProvider = z.infer<typeof DockerProviderSchema>;
type Props (line 29) | interface Props {
FILE: apps/dokploy/components/dashboard/application/general/generic/save-drag-n-drop.tsx
type Props (line 20) | interface Props {
FILE: apps/dokploy/components/dashboard/application/general/generic/save-git-provider.tsx
type GitProvider (line 50) | type GitProvider = z.infer<typeof GitProviderSchema>;
type Props (line 52) | interface Props {
FILE: apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx
type GiteaRepository (line 51) | interface GiteaRepository {
type GiteaBranch (line 60) | interface GiteaBranch {
type GiteaProvider (line 81) | type GiteaProvider = z.infer<typeof GiteaProviderSchema>;
type Props (line 83) | interface Props {
FILE: apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx
type GithubProvider (line 65) | type GithubProvider = z.infer<typeof GithubProviderSchema>;
type Props (line 67) | interface Props {
FILE: apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx
type GitlabProvider (line 67) | type GitlabProvider = z.infer<typeof GitlabProviderSchema>;
type Props (line 69) | interface Props {
FILE: apps/dokploy/components/dashboard/application/general/generic/show.tsx
type TabState (line 25) | type TabState =
type Props (line 34) | interface Props {
FILE: apps/dokploy/components/dashboard/application/general/generic/unauthorized-git-provider.tsx
type Props (line 15) | interface Props {
FILE: apps/dokploy/components/dashboard/application/general/show.tsx
type Props (line 27) | interface Props {
FILE: apps/dokploy/components/dashboard/application/logs/show.tsx
type Props (line 50) | interface Props {
FILE: apps/dokploy/components/dashboard/application/patches/create-file-dialog.tsx
type Props (line 18) | interface Props {
FILE: apps/dokploy/components/dashboard/application/patches/edit-patch-dialog.tsx
type Props (line 18) | interface Props {
FILE: apps/dokploy/components/dashboard/application/patches/patch-editor.tsx
type Props (line 25) | interface Props {
type DirectoryEntry (line 32) | type DirectoryEntry = {
FILE: apps/dokploy/components/dashboard/application/patches/show-patches.tsx
type Props (line 26) | interface Props {
FILE: apps/dokploy/components/dashboard/application/preview-deployments/add-preview-domain.tsx
type Domain (line 45) | type Domain = z.infer<typeof domain>;
type Props (line 47) | interface Props {
FILE: apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx
type Props (line 39) | interface Props {
FILE: apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx
type Schema (line 74) | type Schema = z.infer<typeof schema>;
type Props (line 76) | interface Props {
FILE: apps/dokploy/components/dashboard/application/rollbacks/show-rollback-settings.tsx
type FormValues (line 56) | type FormValues = z.infer<typeof formSchema>;
type Props (line 58) | interface Props {
FILE: apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx
type Props (line 131) | interface Props {
FILE: apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx
type Props (line 31) | interface Props {
FILE: apps/dokploy/components/dashboard/application/schedules/timezones.ts
constant TIMEZONES (line 2) | const TIMEZONES: Record<
function getTimezoneLabel (line 455) | function getTimezoneLabel(value: string | undefined): string {
FILE: apps/dokploy/components/dashboard/application/update-application.tsx
type UpdateApplication (line 37) | type UpdateApplication = z.infer<typeof updateApplicationSchema>;
type Props (line 39) | interface Props {
FILE: apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx
type Props (line 96) | interface Props {
FILE: apps/dokploy/components/dashboard/application/volume-backups/restore-volume-backups.tsx
type Props (line 49) | interface Props {
method onData (line 122) | onData(log) {
method onError (line 133) | onError(error) {
FILE: apps/dokploy/components/dashboard/application/volume-backups/show-volume-backups.tsx
type Props (line 31) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/advanced/add-command.tsx
type Props (line 27) | interface Props {
type AddCommand (line 35) | type AddCommand = z.infer<typeof AddRedirectSchema>;
FILE: apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx
type Props (line 36) | interface Props {
type IsolatedSchema (line 45) | type IsolatedSchema = z.infer<typeof isolatedSchema>;
FILE: apps/dokploy/components/dashboard/compose/delete-service.tsx
type DeleteCompose (line 41) | type DeleteCompose = z.infer<typeof deleteComposeSchema>;
type Props (line 43) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/general/actions.tsx
type Props (line 17) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx
type Props (line 18) | interface Props {
type AddComposeFile (line 26) | type AddComposeFile = z.infer<typeof AddComposeFile>;
FILE: apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx
type BitbucketProvider (line 66) | type BitbucketProvider = z.infer<typeof BitbucketProviderSchema>;
type Props (line 68) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/general/generic/save-git-provider-compose.tsx
type GitProvider (line 50) | type GitProvider = z.infer<typeof GitProviderSchema>;
type Props (line 52) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/general/generic/save-gitea-provider-compose.tsx
type GiteaProvider (line 66) | type GiteaProvider = z.infer<typeof GiteaProviderSchema>;
type Props (line 68) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx
type GithubProvider (line 65) | type GithubProvider = z.infer<typeof GithubProviderSchema>;
type Props (line 67) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/general/generic/save-gitlab-provider-compose.tsx
type GitlabProvider (line 67) | type GitlabProvider = z.infer<typeof GitlabProviderSchema>;
type Props (line 69) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/general/generic/show.tsx
type TabState (line 24) | type TabState = "github" | "git" | "raw" | "gitlab" | "bitbucket" | "git...
type Props (line 25) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx
type Props (line 28) | interface Props {
type Schema (line 37) | type Schema = z.infer<typeof schema>;
FILE: apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx
type Props (line 17) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/general/show.tsx
type Props (line 13) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/logs/show-stack.tsx
type Props (line 35) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/logs/show.tsx
type Props (line 34) | interface Props {
FILE: apps/dokploy/components/dashboard/compose/update-compose.tsx
type UpdateCompose (line 37) | type UpdateCompose = z.infer<typeof updateComposeSchema>;
type Props (line 39) | interface Props {
FILE: apps/dokploy/components/dashboard/database/backups/handle-backup.tsx
type CacheType (line 66) | type CacheType = "cache" | "fetch";
type DatabaseType (line 68) | type DatabaseType = "postgres" | "mariadb" | "mysql" | "mongo" | "web-se...
type Props (line 178) | interface Props {
FILE: apps/dokploy/components/dashboard/database/backups/restore-backup.tsx
type DatabaseType (line 68) | type DatabaseType =
type Props (line 72) | interface Props {
method onData (line 263) | onData(log) {
method onError (line 274) | onError(error) {
FILE: apps/dokploy/components/dashboard/database/backups/show-backups.tsx
type Props (line 40) | interface Props {
FILE: apps/dokploy/components/dashboard/deployments/show-deployments-table.tsx
type DeploymentRow (line 48) | type DeploymentRow =
function getServiceInfo (line 67) | function getServiceInfo(d: DeploymentRow) {
function ShowDeploymentsTable (line 97) | function ShowDeploymentsTable() {
FILE: apps/dokploy/components/dashboard/deployments/show-queue-table.tsx
type QueueRow (line 19) | type QueueRow =
function formatTs (line 42) | function formatTs(ts?: number): string {
function getJobLabel (line 48) | function getJobLabel(row: QueueRow): string {
function ShowQueueTable (line 68) | function ShowQueueTable(props: { embedded?: boolean }) {
FILE: apps/dokploy/components/dashboard/docker/config/show-container-config.tsx
type Props (line 13) | interface Props {
FILE: apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx
type Props (line 21) | interface Props {
FILE: apps/dokploy/components/dashboard/docker/logs/line-count-filter.tsx
type LineCountFilterProps (line 23) | interface LineCountFilterProps {
function LineCountFilter (line 29) | function LineCountFilter({
FILE: apps/dokploy/components/dashboard/docker/logs/show-docker-modal-logs.tsx
type Props (line 22) | interface Props {
FILE: apps/dokploy/components/dashboard/docker/logs/show-docker-modal-stack-logs.tsx
type Props (line 22) | interface Props {
FILE: apps/dokploy/components/dashboard/docker/logs/since-logs-filter.tsx
type TimeFilter (line 19) | type TimeFilter = "all" | "1h" | "6h" | "24h" | "168h" | "720h";
type SinceLogsFilterProps (line 48) | interface SinceLogsFilterProps {
function SinceLogsFilter (line 56) | function SinceLogsFilter({
FILE: apps/dokploy/components/dashboard/docker/logs/status-logs-filter.tsx
type StatusLogsFilterProps (line 19) | interface StatusLogsFilterProps {
function StatusLogsFilter (line 30) | function StatusLogsFilter({
FILE: apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx
type LogLineProps (line 14) | interface LogLineProps {
function TerminalLine (line 22) | function TerminalLine({ log, noTimestamp, searchTerm }: LogLineProps) {
FILE: apps/dokploy/components/dashboard/docker/logs/utils.ts
type LogType (line 1) | type LogType = "error" | "warning" | "success" | "info" | "debug";
type LogVariant (line 2) | type LogVariant = "red" | "yellow" | "green" | "blue" | "orange";
type LogLine (line 4) | interface LogLine {
type LogStyle (line 10) | interface LogStyle {
constant LOG_STYLES (line 16) | const LOG_STYLES: Record<LogType, LogStyle> = {
function parseLogs (line 44) | function parseLogs(logString: string): LogLine[] {
FILE: apps/dokploy/components/dashboard/docker/show/show-containers.tsx
type Container (line 39) | type Container = NonNullable<
type Props (line 43) | interface Props {
FILE: apps/dokploy/components/dashboard/docker/terminal/docker-terminal-modal.tsx
type Props (line 22) | interface Props {
FILE: apps/dokploy/components/dashboard/docker/terminal/docker-terminal.tsx
type Props (line 9) | interface Props {
FILE: apps/dokploy/components/dashboard/file-system/show-traefik-file.tsx
type UpdateServerMiddlewareConfig (line 28) | type UpdateServerMiddlewareConfig = z.infer<
type Props (line 32) | interface Props {
FILE: apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx
type Props (line 15) | interface Props {
FILE: apps/dokploy/components/dashboard/impersonation/impersonation-bar.tsx
type User (line 50) | type User = typeof authClient.$Infer.Session.user;
FILE: apps/dokploy/components/dashboard/mariadb/general/show-external-mariadb-credentials.tsx
type DockerProvider (line 43) | type DockerProvider = z.infer<typeof DockerProviderSchema>;
type Props (line 45) | interface Props {
FILE: apps/dokploy/components/dashboard/mariadb/general/show-general-mariadb.tsx
type Props (line 19) | interface Props {
method onData (line 51) | onData(log) {
method onError (line 62) | onError(error) {
FILE: apps/dokploy/components/dashboard/mariadb/general/show-internal-mariadb-credentials.tsx
type Props (line 7) | interface Props {
FILE: apps/dokploy/components/dashboard/mariadb/update-mariadb.tsx
type UpdateMariadb (line 37) | type UpdateMariadb = z.infer<typeof updateMariadbSchema>;
type Props (line 39) | interface Props {
FILE: apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx
type DockerProvider (line 43) | type DockerProvider = z.infer<typeof DockerProviderSchema>;
type Props (line 45) | interface Props {
FILE: apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx
type Props (line 19) | interface Props {
method onData (line 51) | onData(log) {
method onError (line 63) | onError(error) {
FILE: apps/dokploy/components/dashboard/mongo/general/show-internal-mongo-credentials.tsx
type Props (line 7) | interface Props {
FILE: apps/dokploy/components/dashboard/mongo/update-mongo.tsx
type UpdateMongo (line 37) | type UpdateMongo = z.infer<typeof updateMongoSchema>;
type Props (line 39) | interface Props {
FILE: apps/dokploy/components/dashboard/monitoring/free/container/docker-block-chart.tsx
type Props (line 13) | interface Props {
type CustomTooltipProps (line 75) | interface CustomTooltipProps {
FILE: apps/dokploy/components/dashboard/monitoring/free/container/docker-cpu-chart.tsx
type Props (line 13) | interface Props {
type CustomTooltipProps (line 61) | interface CustomTooltipProps {
FILE: apps/dokploy/components/dashboard/monitoring/free/container/docker-disk-chart.tsx
type Props (line 13) | interface Props {
type CustomTooltipProps (line 77) | interface CustomTooltipProps {
FILE: apps/dokploy/components/dashboard/monitoring/free/container/docker-memory-chart.tsx
type Props (line 14) | interface Props {
type CustomTooltipProps (line 66) | interface CustomTooltipProps {
FILE: apps/dokploy/components/dashboard/monitoring/free/container/docker-network-chart.tsx
type Props (line 13) | interface Props {
type CustomTooltipProps (line 71) | interface CustomTooltipProps {
FILE: apps/dokploy/components/dashboard/monitoring/free/container/show-free-compose-monitoring.tsx
type Props (line 26) | interface Props {
FILE: apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx
type Props (line 43) | interface Props {
type DockerStats (line 47) | interface DockerStats {
type DockerStatsJSON (line 85) | type DockerStatsJSON = {
FILE: apps/dokploy/components/dashboard/monitoring/paid/container/container-block-chart.tsx
type ContainerMetric (line 18) | interface ContainerMetric {
type Props (line 28) | interface Props {
FILE: apps/dokploy/components/dashboard/monitoring/paid/container/container-cpu-chart.tsx
type ContainerMetric (line 18) | interface ContainerMetric {
type Props (line 23) | interface Props {
FILE: apps/dokploy/components/dashboard/monitoring/paid/container/container-memory-chart.tsx
type ContainerMetric (line 18) | interface ContainerMetric {
type Props (line 29) | interface Props {
FILE: apps/dokploy/components/dashboard/monitoring/paid/container/container-network-chart.tsx
type ContainerMetric (line 18) | interface ContainerMetric {
type Props (line 28) | interface Props {
type FormattedMetric (line 32) | interface FormattedMetric {
FILE: apps/dokploy/components/dashboard/monitoring/paid/container/show-paid-compose-monitoring.tsx
type Props (line 27) | interface Props {
FILE: apps/dokploy/components/dashboard/monitoring/paid/container/show-paid-container-monitoring.tsx
constant REFRESH_INTERVALS (line 17) | const REFRESH_INTERVALS = {
constant DATA_POINTS_OPTIONS (line 24) | const DATA_POINTS_OPTIONS = {
type ContainerMetric (line 35) | interface ContainerMetric {
type Props (line 63) | interface Props {
FILE: apps/dokploy/components/dashboard/monitoring/paid/servers/cpu-chart.tsx
type CPUChartProps (line 18) | interface CPUChartProps {
function CPUChart (line 29) | function CPUChart({ data }: CPUChartProps) {
FILE: apps/dokploy/components/dashboard/monitoring/paid/servers/disk-chart.tsx
type RadialChartProps (line 20) | interface RadialChartProps {
function DiskChart (line 24) | function DiskChart({ data }: RadialChartProps) {
FILE: apps/dokploy/components/dashboard/monitoring/paid/servers/memory-chart.tsx
type MemoryChartProps (line 16) | interface MemoryChartProps {
function MemoryChart (line 27) | function MemoryChart({ data }: MemoryChartProps) {
FILE: apps/dokploy/components/dashboard/monitoring/paid/servers/network-chart.tsx
type NetworkChartProps (line 18) | interface NetworkChartProps {
function NetworkChart (line 33) | function NetworkChart({ data }: NetworkChartProps) {
FILE: apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx
constant REFRESH_INTERVALS (line 16) | const REFRESH_INTERVALS = {
constant DATA_POINTS_OPTIONS (line 23) | const DATA_POINTS_OPTIONS = {
type SystemMetrics (line 34) | interface SystemMetrics {
type Props (line 55) | interface Props {
FILE: apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx
type DockerProvider (line 43) | type DockerProvider = z.infer<typeof DockerProviderSchema>;
type Props (line 45) | interface Props {
FILE: apps/dokploy/components/dashboard/mysql/general/show-general-mysql.tsx
type Props (line 19) | interface Props {
method onData (line 50) | onData(log) {
method onError (line 61) | onError(error) {
FILE: apps/dokploy/components/dashboard/mysql/general/show-internal-mysql-credentials.tsx
type Props (line 7) | interface Props {
FILE: apps/dokploy/components/dashboard/mysql/update-mysql.tsx
type UpdateMysql (line 37) | type UpdateMysql = z.infer<typeof updateMysqlSchema>;
type Props (line 39) | interface Props {
FILE: apps/dokploy/components/dashboard/organization/handle-organization.tsx
type OrganizationFormValues (line 36) | type OrganizationFormValues = z.infer<typeof organizationSchema>;
type Props (line 38) | interface Props {
function AddOrganization (line 43) | function AddOrganization({ organizationId }: Props) {
FILE: apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx
type Props (line 33) | interface Props {
type AddDockerImage (line 38) | type AddDockerImage = z.infer<typeof addDockerImage>;
FILE: apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx
type DockerProvider (line 43) | type DockerProvider = z.infer<typeof DockerProviderSchema>;
type Props (line 45) | interface Props {
FILE: apps/dokploy/components/dashboard/postgres/general/show-general-postgres.tsx
type Props (line 19) | interface Props {
method onData (line 51) | onData(log) {
method onError (line 62) | onError(error) {
FILE: apps/dokploy/components/dashboard/postgres/general/show-internal-postgres-credentials.tsx
type Props (line 7) | interface Props {
FILE: apps/dokploy/components/dashboard/postgres/update-postgres.tsx
type UpdatePostgres (line 37) | type UpdatePostgres = z.infer<typeof updatePostgresSchema>;
type Props (line 39) | interface Props {
FILE: apps/dokploy/components/dashboard/project/add-ai-assistant.tsx
type Props (line 3) | interface Props {
FILE: apps/dokploy/components/dashboard/project/add-application.tsx
type AddTemplate (line 64) | type AddTemplate = z.infer<typeof AddTemplateSchema>;
type Props (line 66) | interface Props {
FILE: apps/dokploy/components/dashboard/project/add-compose.tsx
type AddCompose (line 65) | type AddCompose = z.infer<typeof AddComposeSchema>;
type Props (line 67) | interface Props {
FILE: apps/dokploy/components/dashboard/project/add-database.tsx
type DbType (line 55) | type DbType = z.infer<typeof mySchema>["type"];
type AddDatabase (line 171) | type AddDatabase = z.infer<typeof mySchema>;
type Props (line 173) | interface Props {
FILE: apps/dokploy/components/dashboard/project/add-template.tsx
constant TEMPLATE_BASE_URL_KEY (line 73) | const TEMPLATE_BASE_URL_KEY = "dokploy_template_base_url";
type Props (line 75) | interface Props {
FILE: apps/dokploy/components/dashboard/project/advanced-environment-selector.tsx
type Environment (line 29) | type Environment = Awaited<
type AdvancedEnvironmentSelectorProps (line 32) | interface AdvancedEnvironmentSelectorProps {
FILE: apps/dokploy/components/dashboard/project/ai/step-two.tsx
type StepProps (line 21) | interface StepProps {
FILE: apps/dokploy/components/dashboard/project/ai/template-generator.tsx
type EnvVariable (line 31) | interface EnvVariable {
type Domain (line 36) | interface Domain {
type Details (line 42) | interface Details {
type Mount (line 53) | interface Mount {
type TemplateInfo (line 58) | interface TemplateInfo {
type Props (line 92) | interface Props {
FILE: apps/dokploy/components/dashboard/project/duplicate-project.tsx
type Services (line 27) | type Services = {
type DuplicateProjectProps (line 44) | interface DuplicateProjectProps {
FILE: apps/dokploy/components/dashboard/project/environment-variables.tsx
type UpdateEnvironment (line 34) | type UpdateEnvironment = z.infer<typeof updateEnvironmentSchema>;
type Props (line 36) | interface Props {
FILE: apps/dokploy/components/dashboard/projects/handle-project.tsx
type AddProject (line 57) | type AddProject = z.infer<typeof AddProjectSchema>;
type Props (line 59) | interface Props {
FILE: apps/dokploy/components/dashboard/projects/project-environment.tsx
type UpdateProject (line 34) | type UpdateProject = z.infer<typeof updateProjectSchema>;
type Props (line 36) | interface Props {
FILE: apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx
type DockerProvider (line 43) | type DockerProvider = z.infer<typeof DockerProviderSchema>;
type Props (line 45) | interface Props {
FILE: apps/dokploy/components/dashboard/redis/general/show-general-redis.tsx
type Props (line 19) | interface Props {
method onData (line 50) | onData(log) {
method onError (line 61) | onError(error) {
FILE: apps/dokploy/components/dashboard/redis/general/show-internal-redis-credentials.tsx
type Props (line 7) | interface Props {
FILE: apps/dokploy/components/dashboard/redis/update-redis.tsx
type UpdateRedis (line 37) | type UpdateRedis = z.infer<typeof updateRedisSchema>;
type Props (line 39) | interface Props {
FILE: apps/dokploy/components/dashboard/requests/request-distribution-chart.tsx
type RequestDistributionChartProps (line 17) | interface RequestDistributionChartProps {
FILE: apps/dokploy/components/dashboard/requests/requests-table.tsx
type RequestsTableProps (line 83) | interface RequestsTableProps {
FILE: apps/dokploy/components/dashboard/requests/show-requests.tsx
type LogEntry (line 39) | type LogEntry = NonNullable<
FILE: apps/dokploy/components/dashboard/requests/status-request-filter.tsx
type DataTableFacetedFilterProps (line 21) | interface DataTableFacetedFilterProps {
function DataTableFacetedFilter (line 32) | function DataTableFacetedFilter({
FILE: apps/dokploy/components/dashboard/search-command.tsx
type SearchServices (line 30) | type SearchServices = Services & {
FILE: apps/dokploy/components/dashboard/settings/api/add-api-key.tsx
type FormValues (line 52) | type FormValues = z.infer<typeof formSchema>;
constant EXPIRATION_OPTIONS (line 54) | const EXPIRATION_OPTIONS = [
constant TIME_WINDOW_OPTIONS (line 63) | const TIME_WINDOW_OPTIONS = [
constant REFILL_INTERVAL_OPTIONS (line 72) | const REFILL_INTERVAL_OPTIONS = [
FILE: apps/dokploy/components/dashboard/settings/billing/show-billing.tsx
constant STARTUP_SERVERS_INCLUDED (line 54) | const STARTUP_SERVERS_INCLUDED = 3;
FILE: apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx
type AddCertificate (line 58) | type AddCertificate = z.infer<typeof addCertificate>;
FILE: apps/dokploy/components/dashboard/settings/certificates/utils.ts
function readLength (line 16) | function readLength(pos: number): { length: number; offset: number } {
function parseTime (line 72) | function parseTime(str: string): Date {
FILE: apps/dokploy/components/dashboard/settings/cluster/nodes/add-node.tsx
type Props (line 17) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx
type Props (line 13) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/cluster/nodes/show-node-data.tsx
type Props (line 12) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes-modal.tsx
type Props (line 6) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx
type Props (line 46) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx
type Props (line 13) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx
type AddRegistry (line 79) | type AddRegistry = z.infer<typeof AddRegistrySchema>;
type Props (line 81) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/destination/constants.ts
constant S3_PROVIDERS (line 1) | const S3_PROVIDERS: Array<{
FILE: apps/dokploy/components/dashboard/settings/destination/handle-destinations.tsx
type AddDestination (line 51) | type AddDestination = z.infer<typeof addDestination>;
type Props (line 53) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/git/bitbucket/add-bitbucket-provider.tsx
type Schema (line 38) | type Schema = z.infer<typeof Schema>;
FILE: apps/dokploy/components/dashboard/settings/git/bitbucket/edit-bitbucket-provider.tsx
type Schema (line 42) | type Schema = z.infer<typeof Schema>;
type Props (line 44) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/git/gitea/add-gitea-provider.tsx
type Schema (line 59) | type Schema = z.infer<typeof Schema>;
FILE: apps/dokploy/components/dashboard/settings/git/gitea/edit-gitea-provider.tsx
type Props (line 42) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/git/github/edit-github-provider.tsx
type Schema (line 38) | type Schema = z.infer<typeof Schema>;
type Props (line 40) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/git/gitlab/add-gitlab-provider.tsx
type Schema (line 56) | type Schema = z.infer<typeof Schema>;
FILE: apps/dokploy/components/dashboard/settings/git/gitlab/edit-gitlab-provider.tsx
type Schema (line 44) | type Schema = z.infer<typeof Schema>;
type Props (line 46) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/handle-ai.tsx
type Schema (line 52) | type Schema = z.infer<typeof Schema>;
type Props (line 54) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/linking-account/linking-account.tsx
constant LINKING_CALLBACK_URL (line 16) | const LINKING_CALLBACK_URL = "/dashboard/settings/profile";
constant TRUSTED_PROVIDERS (line 18) | const TRUSTED_PROVIDERS = ["google", "github"] as const;
type SocialProvider (line 19) | type SocialProvider = (typeof TRUSTED_PROVIDERS)[number];
type AccountItem (line 21) | type AccountItem = {
function providerLabel (line 26) | function providerLabel(providerId: string): string {
function LinkingAccount (line 30) | function LinkingAccount() {
FILE: apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx
type NotificationSchema (line 223) | type NotificationSchema = z.infer<typeof notificationSchema>;
type Props (line 225) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/profile/configure-2fa.tsx
type PasswordForm (line 58) | type PasswordForm = z.infer<typeof PasswordSchema>;
type Step (line 59) | type Step = "password" | "actions" | "backup-codes";
FILE: apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx
type TwoFactorSetupData (line 55) | type TwoFactorSetupData = {
type PasswordForm (line 61) | type PasswordForm = z.infer<typeof PasswordSchema>;
type PinForm (line 62) | type PinForm = z.infer<typeof PinSchema>;
constant USERNAME_PLACEHOLDER (line 64) | const USERNAME_PLACEHOLDER = "%username%";
constant DATE_PLACEHOLDER (line 65) | const DATE_PLACEHOLDER = "%date%";
constant BACKUP_CODES_PLACEHOLDER (line 66) | const BACKUP_CODES_PLACEHOLDER = "%backupCodes%";
FILE: apps/dokploy/components/dashboard/settings/profile/profile-form.tsx
type Profile (line 48) | type Profile = z.infer<typeof profileSchema>;
FILE: apps/dokploy/components/dashboard/settings/servers/actions/show-server-actions.tsx
type Props (line 16) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/actions/show-storage-actions.tsx
type Props (line 14) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx
type Props (line 20) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx
type Props (line 6) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/edit-script.tsx
type Props (line 29) | interface Props {
type Schema (line 39) | type Schema = z.infer<typeof schema>;
FILE: apps/dokploy/components/dashboard/settings/servers/gpu-support.tsx
type GPUSupportProps (line 16) | interface GPUSupportProps {
function GPUSupport (line 20) | function GPUSupport({ serverId }: GPUSupportProps) {
type StatusRowProps (line 243) | interface StatusRowProps {
function StatusRow (line 251) | function StatusRow({
FILE: apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx
type Schema (line 57) | type Schema = z.infer<typeof Schema>;
type Props (line 59) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/security-audit.tsx
type Props (line 15) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx
type Props (line 42) | interface Props {
type Schema (line 80) | type Schema = z.infer<typeof Schema>;
FILE: apps/dokploy/components/dashboard/settings/servers/setup-server.tsx
type Props (line 36) | interface Props {
method onData (line 64) | onData(log) {
method onError (line 75) | onError(error) {
FILE: apps/dokploy/components/dashboard/settings/servers/show-docker-containers-modal.tsx
type Props (line 6) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/show-monitoring-modal.tsx
type Props (line 6) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/show-schedules-modal.tsx
type Props (line 6) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/show-swarm-overview-modal.tsx
type Props (line 6) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/show-traefik-file-system-modal.tsx
type Props (line 6) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/validate-server.tsx
type Props (line 15) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-server.tsx
type Schema (line 47) | type Schema = z.infer<typeof Schema>;
type Props (line 49) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/servers/welcome-stripe/setup.tsx
method onData (line 52) | onData(log) {
method onError (line 63) | onError(error) {
FILE: apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx
type SSHKey (line 31) | type SSHKey = z.infer<typeof sshKeyCreate>;
type Props (line 33) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/tags/handle-tag.tsx
type Tag (line 52) | type Tag = z.infer<typeof TagSchema>;
type HandleTagProps (line 54) | interface HandleTagProps {
FILE: apps/dokploy/components/dashboard/settings/users/add-invitation.tsx
type AddInvitation (line 46) | type AddInvitation = z.infer<typeof addInvitation>;
FILE: apps/dokploy/components/dashboard/settings/users/add-permissions.tsx
type ProjectForPermissions (line 32) | type ProjectForPermissions =
type EnvironmentForPermissions (line 34) | type EnvironmentForPermissions = ProjectForPermissions["environments"][n...
type Environment (line 36) | type Environment = EnvironmentForPermissions;
type Services (line 38) | type Services = {
type AddPermissions (line 172) | type AddPermissions = z.infer<typeof addPermissions>;
type Props (line 174) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/users/change-role.tsx
type ChangeRoleSchema (line 40) | type ChangeRoleSchema = z.infer<typeof changeRoleSchema>;
type Props (line 42) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/web-domain.tsx
type AddServerDomain (line 65) | type AddServerDomain = z.infer<typeof addServerDomain>;
FILE: apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx
type Props (line 38) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/web-server/edit-traefik-env.tsx
type Schema (line 33) | type Schema = z.infer<typeof schema>;
type Props (line 35) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/web-server/local-server-config.tsx
type Schema (line 28) | type Schema = z.infer<typeof Schema>;
constant DEFAULT_LOCAL_SERVER_DATA (line 30) | const DEFAULT_LOCAL_SERVER_DATA: Schema = {
type Props (line 49) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx
type Props (line 40) | interface Props {
type TraefikPortsForm (line 55) | type TraefikPortsForm = z.infer<typeof TraefikPortsSchema>;
FILE: apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx
type Props (line 37) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/web-server/terminal-modal.tsx
type Props (line 24) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/web-server/terminal.tsx
type Props (line 11) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx
type Schema (line 39) | type Schema = z.infer<typeof schema>;
type Props (line 41) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/web-server/update-server.tsx
type Props (line 31) | interface Props {
FILE: apps/dokploy/components/dashboard/settings/web-server/update-webserver.tsx
type ServiceStatus (line 25) | type ServiceStatus = {
type HealthResult (line 30) | type HealthResult = {
type ModalState (line 36) | type ModalState = "idle" | "checking" | "results" | "updating";
FILE: apps/dokploy/components/dashboard/shared/rebuild-database.tsx
type Props (line 18) | interface Props {
FILE: apps/dokploy/components/dashboard/shared/show-database-advanced-settings.tsx
type Props (line 7) | interface Props {
FILE: apps/dokploy/components/dashboard/swarm/applications/columns.tsx
type ApplicationList (line 13) | interface ApplicationList {
FILE: apps/dokploy/components/dashboard/swarm/applications/data-table.tsx
type DataTableProps (line 34) | interface DataTableProps<TData, TValue> {
function DataTable (line 39) | function DataTable<TData, TValue>({
FILE: apps/dokploy/components/dashboard/swarm/applications/show-applications.tsx
type Props (line 15) | interface Props {
FILE: apps/dokploy/components/dashboard/swarm/details/details-card.tsx
type SwarmList (line 9) | interface SwarmList {
type Props (line 19) | interface Props {
function NodeCard (line 24) | function NodeCard({ node, serverId }: Props) {
FILE: apps/dokploy/components/dashboard/swarm/details/show-node-config.tsx
type Props (line 14) | interface Props {
FILE: apps/dokploy/components/dashboard/swarm/monitoring-card.tsx
type Props (line 21) | interface Props {
function SwarmMonitorCard (line 25) | function SwarmMonitorCard({ serverId }: Props) {
FILE: apps/dokploy/components/icons/data-tools-icons.tsx
type Props (line 5) | interface Props {
FILE: apps/dokploy/components/icons/notification-icons.tsx
type Props (line 3) | interface Props {
FILE: apps/dokploy/components/layouts/dashboard-layout.tsx
type Props (line 6) | interface Props {
FILE: apps/dokploy/components/layouts/onboarding-layout.tsx
type Props (line 9) | interface Props {
FILE: apps/dokploy/components/layouts/side.tsx
type AuthQueryOutput (line 96) | type AuthQueryOutput = inferRouterOutputs<AppRouter>["user"]["get"];
type PermissionsOutput (line 97) | type PermissionsOutput =
type EnabledOpts (line 100) | type EnabledOpts = {
type SingleNavItem (line 106) | type SingleNavItem = {
type NavItem (line 118) | type NavItem =
type ExternalLink (line 130) | type ExternalLink = {
type Menu (line 139) | type Menu = {
constant MENU (line 149) | const MENU: Menu = {
function createMenuForAuthUser (line 435) | function createMenuForAuthUser(opts: {
function isActiveRoute (line 483) | function isActiveRoute(opts: {
function findActiveNavItem (line 508) | function findActiveNavItem(
type Props (line 533) | interface Props {
function LogoWrapper (line 537) | function LogoWrapper() {
function SidebarLogo (line 541) | function SidebarLogo() {
function Page (line 864) | function Page({ children }: Props) {
FILE: apps/dokploy/components/layouts/update-server.tsx
constant AUTO_CHECK_UPDATES_INTERVAL_MINUTES (line 14) | const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 7;
FILE: apps/dokploy/components/proprietary/audit-logs/columns.tsx
constant ACTION_CONFIG (line 29) | const ACTION_CONFIG: Record<
constant RESOURCE_LABELS (line 82) | const RESOURCE_LABELS: Record<string, string> = {
function MetadataCell (line 100) | function MetadataCell({ metadata }: { metadata: string | null }) {
FILE: apps/dokploy/components/proprietary/audit-logs/data-table.tsx
constant ACTION_OPTIONS (line 47) | const ACTION_OPTIONS = [
constant RESOURCE_OPTIONS (line 58) | const RESOURCE_OPTIONS = [
constant PAGE_SIZE_OPTIONS (line 76) | const PAGE_SIZE_OPTIONS = [25, 50, 100, 200];
type AuditAction (line 78) | type AuditAction =
type AuditResourceType (line 87) | type AuditResourceType =
type AuditLogFilters (line 104) | interface AuditLogFilters {
type DataTableProps (line 112) | interface DataTableProps {
function DataTable (line 128) | function DataTable({
FILE: apps/dokploy/components/proprietary/audit-logs/show-audit-logs.tsx
function AuditLogsContent (line 15) | function AuditLogsContent() {
function ShowAuditLogs (line 84) | function ShowAuditLogs() {
FILE: apps/dokploy/components/proprietary/auth/sign-in-with-github.tsx
function SignInWithGithub (line 8) | function SignInWithGithub() {
FILE: apps/dokploy/components/proprietary/auth/sign-in-with-google.tsx
function SignInWithGoogle (line 8) | function SignInWithGoogle() {
FILE: apps/dokploy/components/proprietary/enterprise-feature-gate.tsx
type EnterpriseFeatureLockedProps (line 15) | interface EnterpriseFeatureLockedProps {
function EnterpriseFeatureLocked (line 32) | function EnterpriseFeatureLocked({
type EnterpriseFeatureGateProps (line 77) | interface EnterpriseFeatureGateProps {
function EnterpriseFeatureGate (line 89) | function EnterpriseFeatureGate({
FILE: apps/dokploy/components/proprietary/license-keys/license-key.tsx
function LicenseKeySettings (line 12) | function LicenseKeySettings() {
FILE: apps/dokploy/components/proprietary/roles/manage-custom-roles.tsx
constant RESOURCE_META (line 52) | const RESOURCE_META: Record<string, { label: string; description: string...
constant ACTION_META (line 169) | const ACTION_META: Record<
constant HIDDEN_RESOURCES (line 425) | const HIDDEN_RESOURCES = ["organization", "invitation", "team", "ac"];
constant ROLE_PRESETS (line 428) | const ROLE_PRESETS: {
type CreateRoleSchema (line 548) | type CreateRoleSchema = z.infer<typeof createRoleSchema>;
type HandleCustomRoleProps (line 580) | interface HandleCustomRoleProps {
function HandleCustomRole (line 586) | function HandleCustomRole({
function MembersBadge (line 924) | function MembersBadge({
function PermissionEditor (line 989) | function PermissionEditor({
FILE: apps/dokploy/components/proprietary/sso/register-oidc-dialog.tsx
constant DEFAULT_SCOPES (line 33) | const DEFAULT_SCOPES = ["openid", "email", "profile"];
type OidcProviderForm (line 59) | type OidcProviderForm = z.infer<typeof oidcProviderSchema>;
type RegisterOidcDialogProps (line 61) | interface RegisterOidcDialogProps {
function parseOidcConfig (line 75) | function parseOidcConfig(oidcConfig: string | null): {
function RegisterOidcDialog (line 97) | function RegisterOidcDialog({
FILE: apps/dokploy/components/proprietary/sso/register-saml-dialog.tsx
type SamlProviderForm (line 64) | type SamlProviderForm = z.infer<typeof samlProviderSchema>;
type RegisterSamlDialogProps (line 66) | interface RegisterSamlDialogProps {
function parseSamlConfig (line 80) | function parseSamlConfig(samlConfig: string | null): {
function RegisterSamlDialog (line 102) | function RegisterSamlDialog({
FILE: apps/dokploy/components/proprietary/sso/sign-in-with-sso.tsx
type SSOEmailForm (line 28) | type SSOEmailForm = z.infer<typeof ssoEmailSchema>;
type SignInWithSSOProps (line 30) | interface SignInWithSSOProps {
function SignInWithSSO (line 35) | function SignInWithSSO({ children }: SignInWithSSOProps) {
FILE: apps/dokploy/components/proprietary/sso/sso-settings.tsx
type ProviderForDetails (line 38) | type ProviderForDetails = {
function parseOidcConfig (line 48) | function parseOidcConfig(config: string | null): {
function parseSamlConfig (line 64) | function parseSamlConfig(
FILE: apps/dokploy/components/proprietary/whitelabeling/whitelabeling-preview.tsx
type WhitelabelingPreviewProps (line 11) | interface WhitelabelingPreviewProps {
function WhitelabelingPreview (line 19) | function WhitelabelingPreview({ config }: WhitelabelingPreviewProps) {
FILE: apps/dokploy/components/proprietary/whitelabeling/whitelabeling-provider.tsx
function WhitelabelingProvider (line 6) | function WhitelabelingProvider() {
FILE: apps/dokploy/components/proprietary/whitelabeling/whitelabeling-settings.tsx
type FormSchema (line 54) | type FormSchema = z.infer<typeof formSchema>;
constant DEFAULT_CSS_TEMPLATE (line 56) | const DEFAULT_CSS_TEMPLATE = `/* =======================================...
function WhitelabelingSettings (line 162) | function WhitelabelingSettings() {
FILE: apps/dokploy/components/shared/ChatwootWidget.tsx
type ChatwootWidgetProps (line 4) | interface ChatwootWidgetProps {
FILE: apps/dokploy/components/shared/advance-breadcrumb.tsx
type ProjectItem (line 39) | type ProjectItem = RouterOutputs["project"]["all"][number];
type ProjectEnvironment (line 40) | type ProjectEnvironment = ProjectItem["environments"][number];
type EnvironmentDetails (line 41) | type EnvironmentDetails = RouterOutputs["environment"]["one"];
type ServiceItem (line 43) | type ServiceItem = {
type NamedService (line 49) | type NamedService = {
type EnvironmentServiceCollections (line 53) | type EnvironmentServiceCollections = {
type ServiceCollections (line 63) | type ServiceCollections = Pick<
constant SERVICE_COLLECTION_KEYS (line 74) | const SERVICE_COLLECTION_KEYS = [
constant SERVICE_QUERY_KEYS (line 84) | const SERVICE_QUERY_KEYS = [
constant SERVICE_ICONS (line 94) | const SERVICE_ICONS: Record<
FILE: apps/dokploy/components/shared/alert-block.tsx
type Props (line 4) | interface Props extends React.ComponentPropsWithoutRef<"div"> {
function AlertBlock (line 30) | function AlertBlock({
FILE: apps/dokploy/components/shared/breadcrumb-sidebar.tsx
type BreadcrumbEntry (line 23) | interface BreadcrumbEntry {
type Props (line 32) | interface Props {
FILE: apps/dokploy/components/shared/code-editor.tsx
function dockerComposeComplete (line 101) | function dockerComposeComplete(
type Props (line 132) | interface Props extends ReactCodeMirrorProps {
FILE: apps/dokploy/components/shared/date-tooltip.tsx
type Props (line 10) | interface Props {
FILE: apps/dokploy/components/shared/dialog-action.tsx
type Props (line 13) | interface Props {
FILE: apps/dokploy/components/shared/drawer-logs.tsx
type Props (line 13) | interface Props {
FILE: apps/dokploy/components/shared/focus-shortcut-input.tsx
type Props (line 4) | type Props = React.ComponentPropsWithoutRef<typeof Input>;
FILE: apps/dokploy/components/shared/logo.tsx
type Props (line 3) | interface Props {
FILE: apps/dokploy/components/shared/status-tooltip.tsx
type Props (line 9) | interface Props {
FILE: apps/dokploy/components/shared/tag-badge.tsx
type TagBadgeProps (line 4) | interface TagBadgeProps {
function TagBadge (line 11) | function TagBadge({ name, color, className, children }: TagBadgeProps) {
FILE: apps/dokploy/components/shared/tag-filter.tsx
type Tag (line 23) | interface Tag {
type TagFilterProps (line 29) | interface TagFilterProps {
function TagFilter (line 36) | function TagFilter({
FILE: apps/dokploy/components/shared/tag-selector.tsx
type Tag (line 22) | interface Tag {
type TagSelectorProps (line 28) | interface TagSelectorProps {
function TagSelector (line 37) | function TagSelector({
FILE: apps/dokploy/components/ui/badge.tsx
type BadgeProps (line 36) | interface BadgeProps
function Badge (line 40) | function Badge({ className, variant, ...props }: BadgeProps) {
FILE: apps/dokploy/components/ui/button.tsx
type ButtonProps (line 36) | interface ButtonProps
FILE: apps/dokploy/components/ui/calendar.tsx
type CalendarProps (line 8) | type CalendarProps = React.ComponentProps<typeof DayPicker>;
function Calendar (line 10) | function Calendar({
FILE: apps/dokploy/components/ui/chart.tsx
constant THEMES (line 7) | const THEMES = { light: "", dark: ".dark" } as const;
type ChartConfig (line 9) | type ChartConfig = {
type ChartContextProps (line 19) | type ChartContextProps = {
function useChart (line 25) | function useChart() {
function getPayloadConfigFromPayload (line 318) | function getPayloadConfigFromPayload(
FILE: apps/dokploy/components/ui/command.tsx
type CommandDialogProps (line 24) | interface CommandDialogProps extends DialogProps {}
FILE: apps/dokploy/components/ui/dropzone.tsx
type DropzoneProps (line 7) | interface DropzoneProps
FILE: apps/dokploy/components/ui/file-tree.tsx
type TreeDataItem (line 12) | interface TreeDataItem {
type TreeProps (line 20) | type TreeProps = React.HTMLAttributes<HTMLDivElement> & {
function walkTreeItems (line 64) | function walkTreeItems(
type TreeItemProps (line 113) | type TreeItemProps = TreeProps & {
FILE: apps/dokploy/components/ui/form.tsx
type FormFieldContextValue (line 18) | type FormFieldContextValue<
type FormItemContextValue (line 65) | type FormItemContextValue = {
FILE: apps/dokploy/components/ui/input.tsx
type InputProps (line 6) | interface InputProps
FILE: apps/dokploy/components/ui/modeToggle.tsx
function ModeToggle (line 7) | function ModeToggle() {
FILE: apps/dokploy/components/ui/number-input.tsx
type UnitConverter (line 5) | interface UnitConverter {
type NumberInputWithStepsProps (line 25) | interface NumberInputWithStepsProps {
FILE: apps/dokploy/components/ui/secrets.tsx
type Props (line 19) | interface Props {
FILE: apps/dokploy/components/ui/sheet.tsx
type SheetContentProps (line 50) | interface SheetContentProps
FILE: apps/dokploy/components/ui/sidebar.tsx
constant SIDEBAR_COOKIE_NAME (line 20) | const SIDEBAR_COOKIE_NAME = "sidebar:state";
constant SIDEBAR_COOKIE_MAX_AGE (line 21) | const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
constant SIDEBAR_WIDTH (line 22) | const SIDEBAR_WIDTH = "16rem";
constant SIDEBAR_WIDTH_MOBILE (line 23) | const SIDEBAR_WIDTH_MOBILE = "18rem";
constant SIDEBAR_WIDTH_ICON (line 24) | const SIDEBAR_WIDTH_ICON = "3rem";
constant SIDEBAR_KEYBOARD_SHORTCUT (line 25) | const SIDEBAR_KEYBOARD_SHORTCUT = "b";
type SidebarContext (line 27) | type SidebarContext = {
function useSidebar (line 39) | function useSidebar() {
FILE: apps/dokploy/components/ui/skeleton.tsx
function Skeleton (line 3) | function Skeleton({
FILE: apps/dokploy/components/ui/sonner.tsx
type ToasterProps (line 4) | type ToasterProps = React.ComponentProps<typeof Sonner>;
FILE: apps/dokploy/components/ui/textarea.tsx
type TextareaProps (line 5) | interface TextareaProps
FILE: apps/dokploy/components/ui/time-badge.tsx
function TimeBadge (line 6) | function TimeBadge() {
FILE: apps/dokploy/drizzle/0000_reflective_puck.sql
type "application" (line 61) | CREATE TABLE IF NOT EXISTS "application" (
type "postgres" (line 101) | CREATE TABLE IF NOT EXISTS "postgres" (
type "user" (line 123) | CREATE TABLE IF NOT EXISTS "user" (
type "admin" (line 140) | CREATE TABLE IF NOT EXISTS "admin" (
type "auth" (line 158) | CREATE TABLE IF NOT EXISTS "auth" (
type "project" (line 168) | CREATE TABLE IF NOT EXISTS "project" (
type "domain" (line 176) | CREATE TABLE IF NOT EXISTS "domain" (
type "mariadb" (line 188) | CREATE TABLE IF NOT EXISTS "mariadb" (
type "mongo" (line 211) | CREATE TABLE IF NOT EXISTS "mongo" (
type "mysql" (line 232) | CREATE TABLE IF NOT EXISTS "mysql" (
type "backup" (line 255) | CREATE TABLE IF NOT EXISTS "backup" (
type "destination" (line 269) | CREATE TABLE IF NOT EXISTS "destination" (
type "deployment" (line 280) | CREATE TABLE IF NOT EXISTS "deployment" (
type "mount" (line 289) | CREATE TABLE IF NOT EXISTS "mount" (
type "certificate" (line 305) | CREATE TABLE IF NOT EXISTS "certificate" (
type "session" (line 315) | CREATE TABLE IF NOT EXISTS "session" (
type "redirect" (line 321) | CREATE TABLE IF NOT EXISTS "redirect" (
type "security" (line 331) | CREATE TABLE IF NOT EXISTS "security" (
type "port" (line 340) | CREATE TABLE IF NOT EXISTS "port" (
type "redis" (line 348) | CREATE TABLE IF NOT EXISTS "redis" (
FILE: apps/dokploy/drizzle/0005_cute_terror.sql
type "registry" (line 7) | CREATE TABLE IF NOT EXISTS "registry" (
FILE: apps/dokploy/drizzle/0014_same_hammerhead.sql
type "compose" (line 14) | CREATE TABLE IF NOT EXISTS "compose" (
FILE: apps/dokploy/drizzle/0019_heavy_freak.sql
type "discord" (line 7) | CREATE TABLE IF NOT EXISTS "discord" (
type "email" (line 12) | CREATE TABLE IF NOT EXISTS "email" (
type "notification" (line 22) | CREATE TABLE IF NOT EXISTS "notification" (
type "slack" (line 38) | CREATE TABLE IF NOT EXISTS "slack" (
type "telegram" (line 44) | CREATE TABLE IF NOT EXISTS "telegram" (
FILE: apps/dokploy/drizzle/0026_known_dormammu.sql
type "ssh-key" (line 1) | CREATE TABLE IF NOT EXISTS "ssh-key" (
FILE: apps/dokploy/drizzle/0033_white_hawkeye.sql
type "git_provider" (line 11) | CREATE TABLE IF NOT EXISTS "git_provider" (
type "bitbucket" (line 19) | CREATE TABLE IF NOT EXISTS "bitbucket" (
type "github" (line 27) | CREATE TABLE IF NOT EXISTS "github" (
type "gitlab" (line 39) | CREATE TABLE IF NOT EXISTS "gitlab" (
FILE: apps/dokploy/drizzle/0037_legal_namor.sql
type "server" (line 1) | CREATE TABLE IF NOT EXISTS "server" (
FILE: apps/dokploy/drizzle/0049_dark_leopardon.sql
type "preview_deployments" (line 2) | CREATE TABLE IF NOT EXISTS "preview_deployments" (
FILE: apps/dokploy/drizzle/0056_majestic_skaar.sql
type "gotify" (line 2) | CREATE TABLE IF NOT EXISTS "gotify" (
FILE: apps/dokploy/drizzle/0066_yielding_echo.sql
type "user_temp" (line 1) | CREATE TABLE "user_temp" (
type "session_temp" (line 33) | CREATE TABLE "session_temp" (
type "account" (line 47) | CREATE TABLE "account" (
type "invitation" (line 68) | CREATE TABLE "invitation" (
type "member" (line 79) | CREATE TABLE "member" (
type "organization" (line 99) | CREATE TABLE "organization" (
type "verification" (line 110) | CREATE TABLE "verification" (
type "two_factor" (line 119) | CREATE TABLE "two_factor" (
type "apikey" (line 126) | CREATE TABLE "apikey" (
type "project" (line 613) | CREATE INDEX IF NOT EXISTS "idx_project_organization" ON "project" ("org...
type "server" (line 614) | CREATE INDEX IF NOT EXISTS "idx_server_organization" ON "server" ("organ...
type "ssh-key" (line 615) | CREATE INDEX IF NOT EXISTS "idx_sshkey_organization" ON "ssh-key" ("orga...
type "destination" (line 616) | CREATE INDEX IF NOT EXISTS "idx_destination_organization" ON "destinatio...
type "registry" (line 617) | CREATE INDEX IF NOT EXISTS "idx_registry_organization" ON "registry" ("o...
type "notification" (line 618) | CREATE INDEX IF NOT EXISTS "idx_notification_organization" ON "notificat...
type "certificate" (line 619) | CREATE INDEX IF NOT EXISTS "idx_certificate_organization" ON "certificat...
type "git_provider" (line 620) | CREATE INDEX IF NOT EXISTS "idx_git_provider_organization" ON "git_provi...
FILE: apps/dokploy/drizzle/0068_complex_rhino.sql
type "ai" (line 1) | CREATE TABLE "ai" (
FILE: apps/dokploy/drizzle/0079_bizarre_wendell_rand.sql
type "gitea" (line 4) | CREATE TABLE "gitea" (
FILE: apps/dokploy/drizzle/0088_illegal_ma_gnuci.sql
type "schedule" (line 3) | CREATE TABLE "schedule" (
FILE: apps/dokploy/drizzle/0095_curly_justice.sql
type "rollback" (line 1) | CREATE TABLE "rollback" (
FILE: apps/dokploy/drizzle/0101_moaning_blazing_skull.sql
type "volume_backup" (line 1) | CREATE TABLE "volume_backup" (
FILE: apps/dokploy/drizzle/0107_loud_kang.sql
type "environment" (line 1) | CREATE TABLE "environment" (
FILE: apps/dokploy/drizzle/0110_red_psynapse.sql
type "ntfy" (line 2) | CREATE TABLE "ntfy" (
FILE: apps/dokploy/drizzle/0118_loose_anita_blake.sql
type "lark" (line 2) | CREATE TABLE "lark" (
FILE: apps/dokploy/drizzle/0129_pale_roughhouse.sql
type "custom" (line 2) | CREATE TABLE "custom" (
FILE: apps/dokploy/drizzle/0133_striped_the_order.sql
type "webServerSettings" (line 1) | CREATE TABLE "webServerSettings" (
FILE: apps/dokploy/drizzle/0135_illegal_magik.sql
type "pushover" (line 2) | CREATE TABLE "pushover" (
FILE: apps/dokploy/drizzle/0137_colossal_sally_floyd.sql
type "sso_provider" (line 1) | CREATE TABLE "sso_provider" (
FILE: apps/dokploy/drizzle/0138_pretty_ironclad.sql
type "resend" (line 2) | CREATE TABLE "resend" (
FILE: apps/dokploy/drizzle/0144_odd_gunslinger.sql
type "teams" (line 2) | CREATE TABLE "teams" (
FILE: apps/dokploy/drizzle/0145_remarkable_titania.sql
type "patch" (line 2) | CREATE TABLE "patch" (
FILE: apps/dokploy/drizzle/0149_rare_radioactive_man.sql
type "organization_role" (line 1) | CREATE TABLE "organization_role" (
type "audit_log" (line 10) | CREATE TABLE "audit_log" (
type "organization_role" (line 27) | CREATE INDEX "organizationRole_organizationId_idx" ON "organization_role...
type "organization_role" (line 28) | CREATE INDEX "organizationRole_role_idx" ON "organization_role" USING bt...
type "audit_log" (line 29) | CREATE INDEX "auditLog_organizationId_idx" ON "audit_log" USING btree ("...
type "audit_log" (line 30) | CREATE INDEX "auditLog_userId_idx" ON "audit_log" USING btree ("user_id")
type "audit_log" (line 31) | CREATE INDEX "auditLog_createdAt_idx" ON "audit_log" USING btree ("creat...
FILE: apps/dokploy/drizzle/0152_odd_firelord.sql
type "project_tag" (line 1) | CREATE TABLE "project_tag" (
type "tag" (line 8) | CREATE TABLE "tag" (
Copy disabled (too large)
Download .json
Condensed preview — 1217 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (18,208K chars).
[
{
"path": ".devcontainer/Dockerfile",
"chars": 447,
"preview": "# Dockerfile for DevContainer\nFROM node:24.4.0-bullseye-slim\n\n# Install essential packages\nRUN apt-get update && apt-get"
},
{
"path": ".devcontainer/devcontainer.json",
"chars": 1090,
"preview": "{\n\t\"name\": \"Dokploy development container\",\n\t\"build\": {\n\t\t\"dockerfile\": \"Dockerfile\",\n\t\t\"context\": \"..\"\n\t},\n\t\"features\":"
},
{
"path": ".dockerignore",
"chars": 38,
"preview": "node_modules\n.git\n.gitignore\n*.md\ndist"
},
{
"path": ".github/CODEOWNERS",
"chars": 81,
"preview": "# These owners will be the default owners for everything in\n* @siumauricio\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 659,
"preview": "# These are supported funding model platforms\n\ngithub: [siumauricio]\npatreon: #\nopen_collective: dokploy\nko_fi: # Replac"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yml",
"chars": 3126,
"preview": "name: Bug Report\ndescription: Create a bug report\nlabels: [\"needs-triage🔍\"]\nbody:\n - type: markdown\n attributes:\n "
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 155,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Questions?\n url: https://github.com/Dokploy/dokploy/discussions\n"
},
{
"path": ".github/ISSUE_TEMPLATE/feature-request.yml",
"chars": 1412,
"preview": "name: Feature Request\ndescription: Suggest a new feature or improvement to the project\nlabels: [\"enhancement\"]\nbody:\n -"
},
{
"path": ".github/pull_request_template.md",
"chars": 651,
"preview": "## What is this PR about?\n\nPlease describe in a short paragraph what this PR is about.\n\n## Checklist\n\nBefore submitting "
},
{
"path": ".github/workflows/create-pr.yml",
"chars": 2620,
"preview": "name: Auto PR to main when version changes\n\non:\n push:\n branches:\n - canary\n\npermissions:\n contents: write\n p"
},
{
"path": ".github/workflows/deploy.yml",
"chars": 3397,
"preview": "name: Build Docker images\n\non:\n push:\n branches: [main, canary]\n workflow_dispatch:\n\njobs:\n build-and-push-cloud-i"
},
{
"path": ".github/workflows/dokploy.yml",
"chars": 4910,
"preview": "name: Dokploy Docker Build\n\non:\n push:\n branches: [main, canary, \"fix/re-apply-database-migration-fix\"]\n workflow_d"
},
{
"path": ".github/workflows/format.yml",
"chars": 430,
"preview": "name: autofix.ci\n\non:\n push:\n branches: [canary]\n pull_request:\n branches: [canary]\n\njobs:\n format:\n runs-on"
},
{
"path": ".github/workflows/monitoring.yml",
"chars": 3264,
"preview": "name: Dokploy Monitoring Build\n\non:\n push:\n branches: [main, canary]\n\nenv:\n IMAGE_NAME: dokploy/monitoring\n\njobs:\n "
},
{
"path": ".github/workflows/pr-quality.yml",
"chars": 394,
"preview": "\nname: PR Quality\n\npermissions:\n contents: read\n issues: read\n pull-requests: write\n\non:\n pull_request_target:\n t"
},
{
"path": ".github/workflows/pull-request.yml",
"chars": 1360,
"preview": "name: Pull Request\n\non:\n pull_request:\n branches: [main, canary]\n\npermissions:\n contents: read\n\njobs:\n pr-check:\n "
},
{
"path": ".github/workflows/sync-openapi-docs.yml",
"chars": 2057,
"preview": "name: Generate and Sync OpenAPI\n\non:\n push:\n branches:\n - canary\n - main\n paths:\n - 'apps/dokploy/"
},
{
"path": ".gitignore",
"chars": 436,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# Dependencies\nnode_modules\n.pnp\n"
},
{
"path": ".nvmrc",
"chars": 6,
"preview": "24.4.0"
},
{
"path": ".vscode/extensions.json",
"chars": 42,
"preview": "{\n\t\"recommendations\": [\"biomejs.biome\"]\n}\n"
},
{
"path": ".vscode/settings.json",
"chars": 195,
"preview": "{\n\t\"editor.formatOnSave\": true,\n\t\"editor.defaultFormatter\": \"biomejs.biome\",\n\t\"editor.codeActionsOnSave\": {\n\t\t\"source.fi"
},
{
"path": "CONTRIBUTING.md",
"chars": 7071,
"preview": "# Contributing\n\nHey, thanks for your interest in contributing to Dokploy! We appreciate your help and taking your time t"
},
{
"path": "Dockerfile",
"chars": 2345,
"preview": "# syntax=docker/dockerfile:1\nFROM node:24.4.0-slim AS base\nENV PNPM_HOME=\"/pnpm\"\nENV PATH=\"$PNPM_HOME:$PATH\"\nRUN corepac"
},
{
"path": "Dockerfile.cloud",
"chars": 1935,
"preview": "# syntax=docker/dockerfile:1\nFROM node:24.4.0-slim AS base\nENV PNPM_HOME=\"/pnpm\"\nENV PATH=\"$PNPM_HOME:$PATH\"\nRUN corepac"
},
{
"path": "Dockerfile.monitoring",
"chars": 923,
"preview": "# syntax=docker/dockerfile:1\n# Build stage\nFROM golang:1.21-alpine3.19 AS builder\n\n# Instalar dependencias necesarias\nRU"
},
{
"path": "Dockerfile.schedule",
"chars": 1129,
"preview": "# syntax=docker/dockerfile:1\nFROM node:24.4.0-slim AS base\nENV PNPM_HOME=\"/pnpm\"\nENV PATH=\"$PNPM_HOME:$PATH\"\nRUN corepac"
},
{
"path": "Dockerfile.server",
"chars": 1075,
"preview": "# syntax=docker/dockerfile:1\nFROM node:24.4.0-slim AS base\nENV PNPM_HOME=\"/pnpm\"\nENV PATH=\"$PNPM_HOME:$PATH\"\nRUN corepac"
},
{
"path": "GUIDES.md",
"chars": 1598,
"preview": "# Docker\n\nHere's how to install docker on different operating systems:\n\n## macOS\n\n1. Visit [Docker Desktop for Mac](http"
},
{
"path": "LICENSE.MD",
"chars": 1015,
"preview": "Copyright 2026-present Dokploy Technology, Inc.\n\nPortions of this software are licensed as follows:\n\n* All content that "
},
{
"path": "LICENSE_PROPRIETARY.md",
"chars": 2118,
"preview": "The Dokploy Source Available license (DSAL) version 1.0\n\nCopyright (c) 2026-present Dokploy Technology, Inc.\n\nWith regar"
},
{
"path": "README.md",
"chars": 2639,
"preview": "<div align=\"center\">\n <a href=\"https://dokploy.com\">\n <img src=\".github/sponsors/logo.png\" alt=\"Dokploy - Open Sourc"
},
{
"path": "SECURITY.md",
"chars": 1699,
"preview": "# Dokploy Security Policy\n\nAt Dokploy, security is a top priority. We appreciate the help of security researchers and th"
},
{
"path": "TERMS_AND_CONDITIONS.md",
"chars": 1637,
"preview": "# Terms & Conditions\n\n**Dokploy core** is a free and open-source solution intended as an alternative to established clou"
},
{
"path": "apps/api/.gitignore",
"chars": 312,
"preview": "# dev\n.yarn/\n!.yarn/releases\n.vscode/*\n!.vscode/launch.json\n!.vscode/*.code-snippets\n.idea/workspace.xml\n.idea/usage.sta"
},
{
"path": "apps/api/README.md",
"chars": 68,
"preview": "```\nnpm install\nnpm run dev\n```\n\n```\nopen http://localhost:3000\n```\n"
},
{
"path": "apps/api/package.json",
"chars": 855,
"preview": "{\n\t\"name\": \"@dokploy/api\",\n\t\"version\": \"0.0.1\",\n\t\"type\": \"module\",\n\t\"scripts\": {\n\t\t\"dev\": \"PORT=4000 tsx watch src/index"
},
{
"path": "apps/api/src/index.ts",
"chars": 4959,
"preview": "import { serve } from \"@hono/node-server\";\nimport { Hono } from \"hono\";\nimport \"dotenv/config\";\nimport { zValidator } fr"
},
{
"path": "apps/api/src/logger.ts",
"chars": 139,
"preview": "import pino from \"pino\";\n\nexport const logger = pino({\n\ttransport: {\n\t\ttarget: \"pino-pretty\",\n\t\toptions: {\n\t\t\tcolorize: "
},
{
"path": "apps/api/src/schema.ts",
"chars": 1346,
"preview": "import { z } from \"zod\";\n\nexport const deployJobSchema = z.discriminatedUnion(\"applicationType\", [\n\tz.object({\n\t\tapplica"
},
{
"path": "apps/api/src/service.ts",
"chars": 6394,
"preview": "import { logger } from \"./logger.js\";\n\nconst baseUrl = process.env.INNGEST_BASE_URL ?? \"\";\nconst signingKey = process.en"
},
{
"path": "apps/api/src/utils.ts",
"chars": 2697,
"preview": "import {\n\tdeployApplication,\n\tdeployCompose,\n\tdeployPreviewApplication,\n\trebuildApplication,\n\trebuildCompose,\n\trebuildPr"
},
{
"path": "apps/api/tsconfig.json",
"chars": 368,
"preview": "{\n\t\"compilerOptions\": {\n\t\t\"target\": \"ESNext\",\n\t\t\"module\": \"ESNext\",\n\t\t\"moduleResolution\": \"Node\",\n\t\t\"strict\": true,\n\t\t\"s"
},
{
"path": "apps/dokploy/.dockerignore",
"chars": 38,
"preview": "node_modules\n.git\n.gitignore\n*.md\ndist"
},
{
"path": "apps/dokploy/.gitignore",
"chars": 738,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": "apps/dokploy/__test__/cluster/upload.test.ts",
"chars": 8457,
"preview": "import type { Registry } from \"@dokploy/server\";\nimport { getRegistryTag } from \"@dokploy/server\";\nimport { describe, ex"
},
{
"path": "apps/dokploy/__test__/compose/compose.test.ts",
"chars": 8995,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToAllProperties } from \"@dokploy/server\";"
},
{
"path": "apps/dokploy/__test__/compose/config/config-root.test.ts",
"chars": 3871,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToConfigsRoot, generateRandomHash } from "
},
{
"path": "apps/dokploy/__test__/compose/config/config-service.test.ts",
"chars": 4394,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport {\n\taddSuffixToConfigsInServices,\n\tgenerateRandomHash"
},
{
"path": "apps/dokploy/__test__/compose/config/config.test.ts",
"chars": 4939,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToAllConfigs, generateRandomHash } from \""
},
{
"path": "apps/dokploy/__test__/compose/domain/host-rule-format.test.ts",
"chars": 6863,
"preview": "import type { Domain } from \"@dokploy/server\";\nimport { createDomainLabels } from \"@dokploy/server\";\nimport { describe, "
},
{
"path": "apps/dokploy/__test__/compose/domain/labels.test.ts",
"chars": 7663,
"preview": "import type { Domain } from \"@dokploy/server\";\nimport { createDomainLabels } from \"@dokploy/server\";\nimport { describe, "
},
{
"path": "apps/dokploy/__test__/compose/domain/network-root.test.ts",
"chars": 1074,
"preview": "import { addDokployNetworkToRoot } from \"@dokploy/server\";\nimport { describe, expect, it } from \"vitest\";\n\ndescribe(\"add"
},
{
"path": "apps/dokploy/__test__/compose/domain/network-service.test.ts",
"chars": 1196,
"preview": "import { addDokployNetworkToService } from \"@dokploy/server\";\nimport { describe, expect, it } from \"vitest\";\n\ndescribe(\""
},
{
"path": "apps/dokploy/__test__/compose/network/network-root.test.ts",
"chars": 6050,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToNetworksRoot, generateRandomHash } from"
},
{
"path": "apps/dokploy/__test__/compose/network/network-service.test.ts",
"chars": 6065,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport {\n\taddSuffixToServiceNetworks,\n\tgenerateRandomHash,\n"
},
{
"path": "apps/dokploy/__test__/compose/network/network.test.ts",
"chars": 6608,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport {\n\taddSuffixToAllNetworks,\n\taddSuffixToNetworksRoot,"
},
{
"path": "apps/dokploy/__test__/compose/secrets/secret-root.test.ts",
"chars": 2320,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToSecretsRoot, generateRandomHash } from "
},
{
"path": "apps/dokploy/__test__/compose/secrets/secret-services.test.ts",
"chars": 2497,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport {\n\taddSuffixToSecretsInServices,\n\tgenerateRandomHash"
},
{
"path": "apps/dokploy/__test__/compose/secrets/secret.test.ts",
"chars": 3076,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToAllSecrets } from \"@dokploy/server\";\nim"
},
{
"path": "apps/dokploy/__test__/compose/service/service-container-name.test.ts",
"chars": 1599,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToServiceNames, generateRandomHash } from"
},
{
"path": "apps/dokploy/__test__/compose/service/service-depends-on.test.ts",
"chars": 4356,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToServiceNames, generateRandomHash } from"
},
{
"path": "apps/dokploy/__test__/compose/service/service-extends.test.ts",
"chars": 3640,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToServiceNames, generateRandomHash } from"
},
{
"path": "apps/dokploy/__test__/compose/service/service-links.test.ts",
"chars": 2104,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToServiceNames, generateRandomHash } from"
},
{
"path": "apps/dokploy/__test__/compose/service/service-names.test.ts",
"chars": 1292,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToServiceNames, generateRandomHash } from"
},
{
"path": "apps/dokploy/__test__/compose/service/service.test.ts",
"chars": 6382,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport {\n\taddSuffixToAllServiceNames,\n\taddSuffixToServiceNa"
},
{
"path": "apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts",
"chars": 2075,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToServiceNames, generateRandomHash } from"
},
{
"path": "apps/dokploy/__test__/compose/volume/volume-2.test.ts",
"chars": 33547,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport {\n\taddSuffixToAllVolumes,\n\taddSuffixToVolumesRoot,\n\t"
},
{
"path": "apps/dokploy/__test__/compose/volume/volume-root.test.ts",
"chars": 3731,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToVolumesRoot, generateRandomHash } from "
},
{
"path": "apps/dokploy/__test__/compose/volume/volume-services.test.ts",
"chars": 1832,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport {\n\taddSuffixToVolumesInServices,\n\tgenerateRandomHash"
},
{
"path": "apps/dokploy/__test__/compose/volume/volume.test.ts",
"chars": 5999,
"preview": "import type { ComposeSpecification } from \"@dokploy/server\";\nimport { addSuffixToAllVolumes } from \"@dokploy/server\";\nim"
},
{
"path": "apps/dokploy/__test__/deploy/application.command.test.ts",
"chars": 8339,
"preview": "import * as adminService from \"@dokploy/server/services/admin\";\nimport * as applicationService from \"@dokploy/server/ser"
},
{
"path": "apps/dokploy/__test__/deploy/application.real.test.ts",
"chars": 14328,
"preview": "import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport type { ApplicationNested } from \"@dokploy/ser"
},
{
"path": "apps/dokploy/__test__/deploy/github.test.ts",
"chars": 11151,
"preview": "import { describe, expect, it } from \"vitest\";\nimport {\n\textractCommitMessage,\n\textractImageName,\n\textractImageTag,\n\text"
},
{
"path": "apps/dokploy/__test__/deploy/soft-serve.test.ts",
"chars": 1548,
"preview": "import { describe, expect, it } from \"vitest\";\nimport {\n\textractBranchName,\n\textractCommitMessage,\n\textractHash,\n\tgetPro"
},
{
"path": "apps/dokploy/__test__/drop/drop.test.ts",
"chars": 12435,
"preview": "import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { ApplicationNested } from \"@dokploy/server"
},
{
"path": "apps/dokploy/__test__/drop/zips/folder1/folder1.txt",
"chars": 12,
"preview": "Gogogogogogo"
},
{
"path": "apps/dokploy/__test__/drop/zips/folder2/folder2.txt",
"chars": 11,
"preview": "gogogogogog"
},
{
"path": "apps/dokploy/__test__/drop/zips/folder3/file3.txt",
"chars": 18,
"preview": "gogogogogogogogogo"
},
{
"path": "apps/dokploy/__test__/drop/zips/test.txt",
"chars": 12,
"preview": "dsafasdfasdf"
},
{
"path": "apps/dokploy/__test__/env/environment-access-fallback.test.ts",
"chars": 7475,
"preview": "import { describe, expect, it } from \"vitest\";\n\n// Type definitions matching the project structure\ntype Environment = {\n"
},
{
"path": "apps/dokploy/__test__/env/environment.test.ts",
"chars": 16496,
"preview": "import {\n\tprepareEnvironmentVariables,\n\tprepareEnvironmentVariablesForShell,\n} from \"@dokploy/server/index\";\nimport { de"
},
{
"path": "apps/dokploy/__test__/env/shared.test.ts",
"chars": 6549,
"preview": "import { prepareEnvironmentVariables } from \"@dokploy/server/index\";\nimport { describe, expect, it } from \"vitest\";\n\ncon"
},
{
"path": "apps/dokploy/__test__/env/stack-environment.test.ts",
"chars": 4707,
"preview": "import { getEnviromentVariablesObject } from \"@dokploy/server/index\";\nimport { describe, expect, it } from \"vitest\";\n\nco"
},
{
"path": "apps/dokploy/__test__/permissions/check-permission.test.ts",
"chars": 4349,
"preview": "import { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nconst mockMemberData = (\n\trole: string,\n\toverrides: Reco"
},
{
"path": "apps/dokploy/__test__/permissions/enterprise-only-resources.test.ts",
"chars": 1794,
"preview": "import { describe, it, expect } from \"vitest\";\nimport {\n\tenterpriseOnlyResources,\n\tstatements,\n} from \"@dokploy/server/l"
},
{
"path": "apps/dokploy/__test__/permissions/resolve-permissions.test.ts",
"chars": 5325,
"preview": "import { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nconst mockMemberData = (\n\trole: string,\n\toverrides: Reco"
},
{
"path": "apps/dokploy/__test__/permissions/service-access.test.ts",
"chars": 3575,
"preview": "import { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nconst mockMemberData = (\n\trole: string,\n\taccessedService"
},
{
"path": "apps/dokploy/__test__/requests/request.test.ts",
"chars": 6515,
"preview": "import { parseRawConfig, processLogs } from \"@dokploy/server\";\nimport { describe, expect, it } from \"vitest\";\n\nconst sam"
},
{
"path": "apps/dokploy/__test__/server/mechanizeDockerContainer.test.ts",
"chars": 4839,
"preview": "import type { ApplicationNested } from \"@dokploy/server/utils/builders\";\nimport { mechanizeDockerContainer } from \"@dokp"
},
{
"path": "apps/dokploy/__test__/setup.ts",
"chars": 1253,
"preview": "import { vi } from \"vitest\";\n\n/**\n * Mock the DB module so tests that import from @dokploy/server (barrel)\n * never open"
},
{
"path": "apps/dokploy/__test__/templates/config.template.test.ts",
"chars": 13928,
"preview": "import type { Schema } from \"@dokploy/server/templates\";\nimport type { CompleteTemplate } from \"@dokploy/server/template"
},
{
"path": "apps/dokploy/__test__/templates/helpers.template.test.ts",
"chars": 11266,
"preview": "import type { Schema } from \"@dokploy/server/templates\";\nimport { processValue } from \"@dokploy/server/templates/process"
},
{
"path": "apps/dokploy/__test__/traefik/server/update-server-config.test.ts",
"chars": 3057,
"preview": "import { fs, vol } from \"memfs\";\n\nvi.mock(\"node:fs\", () => ({\n\t...fs,\n\tdefault: fs,\n}));\n\nimport type { FileConfig } fro"
},
{
"path": "apps/dokploy/__test__/traefik/traefik.test.ts",
"chars": 7406,
"preview": "import type { ApplicationNested, Domain, Redirect } from \"@dokploy/server\";\nimport { createRouterConfig } from \"@dokploy"
},
{
"path": "apps/dokploy/__test__/utils/backups.test.ts",
"chars": 2402,
"preview": "import { normalizeS3Path } from \"@dokploy/server/utils/backups/utils\";\nimport { describe, expect, test } from \"vitest\";\n"
},
{
"path": "apps/dokploy/__test__/vitest.config.ts",
"chars": 815,
"preview": "import path from \"node:path\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\nimport { defineConfig } from \"vitest/conf"
},
{
"path": "apps/dokploy/__test__/wss/readValidDirectory.test.ts",
"chars": 3058,
"preview": "import path from \"node:path\";\nimport { describe, expect, it, vi } from \"vitest\";\n\nconst BASE = \"/base\";\n\nvi.mock(\"@dokpl"
},
{
"path": "apps/dokploy/__test__/wss/utils.test.ts",
"chars": 4650,
"preview": "import { describe, expect, it } from \"vitest\";\nimport {\n\tisValidContainerId,\n\tisValidSearch,\n\tisValidSince,\n\tisValidTail"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx",
"chars": 7029,
"preview": "import { Settings } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { AlertBlock } from \"@/components/shar"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx",
"chars": 6943,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { Server } from \"luc"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/endpoint-spec-form.tsx",
"chars": 4116,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect, useStat"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/health-check-form.tsx",
"chars": 7181,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect, useStat"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/index.ts",
"chars": 589,
"preview": "export { EndpointSpecForm } from \"./endpoint-spec-form\";\nexport { HealthCheckForm } from \"./health-check-form\";\nexport {"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/labels-form.tsx",
"chars": 5105,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect, useStat"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/mode-form.tsx",
"chars": 5007,
"preview": "import { useEffect, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\n"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/network-form.tsx",
"chars": 8722,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect, useStat"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/placement-form.tsx",
"chars": 9106,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect, useStat"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/restart-policy-form.tsx",
"chars": 5931,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect, useStat"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/rollback-config-form.tsx",
"chars": 7047,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect, useStat"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/stop-grace-period-form.tsx",
"chars": 4119,
"preview": "import { useEffect, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\n"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/update-config-form.tsx",
"chars": 7295,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect, useStat"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/cluster/swarm-forms/utils.ts",
"chars": 841,
"preview": "/**\n * Filters out undefined, null, and empty string values from form data\n * Only returns fields that have actual value"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/general/add-command.tsx",
"chars": 4524,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { Plus, Trash2 } fro"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/import/show-import.tsx",
"chars": 9727,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { Code2, Globe2, Har"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/ports/handle-ports.tsx",
"chars": 7346,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { PenBoxIcon, PlusIc"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/ports/show-port.tsx",
"chars": 4329,
"preview": "import { Rss, Trash2 } from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport { AlertBlock } from \"@/components/sha"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/redirects/handle-redirect.tsx",
"chars": 7021,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { PenBoxIcon, PlusIc"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/redirects/show-redirects.tsx",
"chars": 4153,
"preview": "import { Split, Trash2 } from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport { DialogAction } from \"@/components"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/security/handle-security.tsx",
"chars": 4295,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { PenBoxIcon, PlusIc"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/security/show-security.tsx",
"chars": 3876,
"preview": "import { LockKeyhole, Trash2 } from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport { DialogAction } from \"@/comp"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/show-build-server.tsx",
"chars": 8110,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { Server } from \"luc"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/show-resources.tsx",
"chars": 15634,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { InfoIcon, Plus, Tr"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/traefik/show-traefik-config.tsx",
"chars": 2167,
"preview": "import { File, Loader2 } from \"lucide-react\";\nimport { CodeEditor } from \"@/components/shared/code-editor\";\nimport {\n\tCa"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/traefik/update-traefik-config.tsx",
"chars": 5552,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect, useStat"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/volumes/add-volumes.tsx",
"chars": 11165,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { PlusIcon } from \"l"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx",
"chars": 6507,
"preview": "import { Package, Trash2 } from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport { AlertBlock } from \"@/components"
},
{
"path": "apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx",
"chars": 7589,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { PenBoxIcon } from "
},
{
"path": "apps/dokploy/components/dashboard/application/build/show.tsx",
"chars": 15418,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { Cog } from \"lucide"
},
{
"path": "apps/dokploy/components/dashboard/application/deployments/cancel-queues.tsx",
"chars": 1780,
"preview": "import { Ban } from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport {\n\tAlertDialog,\n\tAlertDialogAction,\n\tAlertDia"
},
{
"path": "apps/dokploy/components/dashboard/application/deployments/clear-deployments.tsx",
"chars": 1977,
"preview": "import { Paintbrush } from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport {\n\tAlertDialog,\n\tAlertDialogAction,\n\tA"
},
{
"path": "apps/dokploy/components/dashboard/application/deployments/kill-build.tsx",
"chars": 1639,
"preview": "import { Scissors } from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport {\n\tAlertDialog,\n\tAlertDialogAction,\n\tAle"
},
{
"path": "apps/dokploy/components/dashboard/application/deployments/refresh-token.tsx",
"chars": 1866,
"preview": "import { RefreshCcw } from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport {\n\tAlertDialog,\n\tAlertDialogAction,\n\tA"
},
{
"path": "apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx",
"chars": 5799,
"preview": "import copy from \"copy-to-clipboard\";\nimport { Check, Copy, Loader2 } from \"lucide-react\";\nimport { useEffect, useRef, u"
},
{
"path": "apps/dokploy/components/dashboard/application/deployments/show-deployments-modal.tsx",
"chars": 1747,
"preview": "import { useState } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Dialog, DialogContent, Dialo"
},
{
"path": "apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx",
"chars": 15857,
"preview": "import {\n\tChevronDown,\n\tChevronUp,\n\tClock,\n\tCopy,\n\tLoader2,\n\tRefreshCcw,\n\tRocketIcon,\n\tSettings,\n\tTrash2,\n} from \"lucide"
},
{
"path": "apps/dokploy/components/dashboard/application/domains/dns-helper-modal.tsx",
"chars": 3223,
"preview": "import { Copy, HelpCircle, Server } from \"lucide-react\";\nimport { toast } from \"sonner\";\nimport { AlertBlock } from \"@/c"
},
{
"path": "apps/dokploy/components/dashboard/application/domains/handle-domain.tsx",
"chars": 21478,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { DatabaseZap, Dices"
},
{
"path": "apps/dokploy/components/dashboard/application/domains/show-domains.tsx",
"chars": 12338,
"preview": "import {\n\tCheckCircle2,\n\tExternalLink,\n\tGlobeIcon,\n\tInfoIcon,\n\tLoader2,\n\tPenBoxIcon,\n\tRefreshCw,\n\tServer,\n\tTrash2,\n\tXCir"
},
{
"path": "apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx",
"chars": 5856,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { EyeIcon, EyeOffIco"
},
{
"path": "apps/dokploy/components/dashboard/application/environment/show.tsx",
"chars": 6162,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect } from \""
},
{
"path": "apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx",
"chars": 15219,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { CheckIcon, Chevron"
},
{
"path": "apps/dokploy/components/dashboard/application/general/generic/save-docker-provider.tsx",
"chars": 3928,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect } from \""
},
{
"path": "apps/dokploy/components/dashboard/application/general/generic/save-drag-n-drop.tsx",
"chars": 3620,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { TrashIcon } from \""
},
{
"path": "apps/dokploy/components/dashboard/application/general/generic/save-git-provider.tsx",
"chars": 9354,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { HelpCircle, KeyRou"
},
{
"path": "apps/dokploy/components/dashboard/application/general/generic/save-gitea-provider.tsx",
"chars": 15437,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { CheckIcon, Chevron"
},
{
"path": "apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx",
"chars": 16167,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { CheckIcon, Chevron"
},
{
"path": "apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx",
"chars": 15707,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { CheckIcon, Chevron"
},
{
"path": "apps/dokploy/components/dashboard/application/general/generic/show.tsx",
"chars": 10695,
"preview": "import { GitBranch, Loader2, UploadCloud } from \"lucide-react\";\nimport Link from \"next/link\";\nimport { useState } from \""
},
{
"path": "apps/dokploy/components/dashboard/application/general/generic/unauthorized-git-provider.tsx",
"chars": 4179,
"preview": "import { AlertCircle, GitBranch, Unlink } from \"lucide-react\";\nimport {\n\tBitbucketIcon,\n\tGiteaIcon,\n\tGithubIcon,\n\tGitIco"
},
{
"path": "apps/dokploy/components/dashboard/application/general/show.tsx",
"chars": 10696,
"preview": "import * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport {\n\tBan,\n\tCheckCircle2,\n\tHammer,\n\tRefreshCcw,\n\tRocket"
},
{
"path": "apps/dokploy/components/dashboard/application/logs/show.tsx",
"chars": 4994,
"preview": "import { Loader2 } from \"lucide-react\";\nimport dynamic from \"next/dynamic\";\nimport { useEffect, useState } from \"react\";"
},
{
"path": "apps/dokploy/components/dashboard/application/patches/create-file-dialog.tsx",
"chars": 2642,
"preview": "import { FilePlus } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { CodeEditor } from \"@/components/shar"
},
{
"path": "apps/dokploy/components/dashboard/application/patches/edit-patch-dialog.tsx",
"chars": 2601,
"preview": "import { Loader2, Pencil } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport { toast } from \"sonn"
},
{
"path": "apps/dokploy/components/dashboard/application/patches/index.ts",
"chars": 64,
"preview": "export * from \"./show-patches\";\nexport * from \"./patch-editor\";\n"
},
{
"path": "apps/dokploy/components/dashboard/application/patches/patch-editor.tsx",
"chars": 9983,
"preview": "import {\n\tArrowLeft,\n\tChevronRight,\n\tFile,\n\tFolder,\n\tLoader2,\n\tSave,\n\tTrash2,\n} from \"lucide-react\";\nimport { useCallbac"
},
{
"path": "apps/dokploy/components/dashboard/application/patches/show-patches.tsx",
"chars": 6348,
"preview": "import { File, FilePlus2, Loader2, Trash2 } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { toast } from"
},
{
"path": "apps/dokploy/components/dashboard/application/preview-deployments/add-preview-domain.tsx",
"chars": 8275,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { Dices } from \"luci"
},
{
"path": "apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx",
"chars": 10421,
"preview": "import * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport {\n\tExternalLink,\n\tFileText,\n\tGitPullRequest,\n\tHammer"
},
{
"path": "apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx",
"chars": 16508,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { HelpCircle, Plus, "
},
{
"path": "apps/dokploy/components/dashboard/application/rollbacks/Backup",
"chars": 3742,
"preview": "Backup\n# license-namedbackups-abxelc\n1. docker ps --filter \"label=com.docker.swarm.service.name=license-namedbackups-abx"
},
{
"path": "apps/dokploy/components/dashboard/application/rollbacks/show-rollback-settings.tsx",
"chars": 6124,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport Link from \"next/link"
},
{
"path": "apps/dokploy/components/dashboard/application/schedules/handle-schedules.tsx",
"chars": 18317,
"preview": "import { standardSchemaResolver } from \"@hookform/resolvers/standard-schema\";\nimport {\n\tCheckIcon,\n\tChevronsUpDown,\n\tDat"
},
{
"path": "apps/dokploy/components/dashboard/application/schedules/show-schedules.tsx",
"chars": 8132,
"preview": "import {\n\tClipboardList,\n\tClock,\n\tLoader2,\n\tPlay,\n\tTerminal,\n\tTrash2,\n} from \"lucide-react\";\nimport { useState } from \"r"
},
{
"path": "apps/dokploy/components/dashboard/application/schedules/timezones.ts",
"chars": 22450,
"preview": "// Complete list of IANA timezones grouped by region\nexport const TIMEZONES: Record<\n\tstring,\n\tArray<{ label: string; va"
},
{
"path": "apps/dokploy/components/dashboard/application/update-application.tsx",
"chars": 4170,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { PenBoxIcon } from "
},
{
"path": "apps/dokploy/components/dashboard/application/volume-backups/handle-volume-backups.tsx",
"chars": 17717,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { DatabaseZap, PenBo"
},
{
"path": "apps/dokploy/components/dashboard/application/volume-backups/restore-volume-backups.tsx",
"chars": 11508,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport copy from \"copy-to-c"
},
{
"path": "apps/dokploy/components/dashboard/application/volume-backups/show-volume-backups.tsx",
"chars": 8190,
"preview": "import {\n\tClipboardList,\n\tDatabaseBackup,\n\tLoader2,\n\tPlay,\n\tTrash2,\n} from \"lucide-react\";\nimport { useState } from \"rea"
},
{
"path": "apps/dokploy/components/dashboard/compose/advanced/add-command.tsx",
"chars": 3337,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect } from \""
},
{
"path": "apps/dokploy/components/dashboard/compose/advanced/add-isolation.tsx",
"chars": 6979,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { AlertTriangle, Loa"
},
{
"path": "apps/dokploy/components/dashboard/compose/delete-service.tsx",
"chars": 7197,
"preview": "import type { ServiceType } from \"@dokploy/server/db/schema\";\nimport { standardSchemaResolver as zodResolver } from \"@ho"
},
{
"path": "apps/dokploy/components/dashboard/compose/general/actions.tsx",
"chars": 7533,
"preview": "import * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } "
},
{
"path": "apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx",
"chars": 4711,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { useEffect, useStat"
},
{
"path": "apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx",
"chars": 15227,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { CheckIcon, Chevron"
},
{
"path": "apps/dokploy/components/dashboard/compose/general/generic/save-git-provider-compose.tsx",
"chars": 9374,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { HelpCircle, KeyRou"
},
{
"path": "apps/dokploy/components/dashboard/compose/general/generic/save-gitea-provider-compose.tsx",
"chars": 14629,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { CheckIcon, Chevron"
},
{
"path": "apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx",
"chars": 16290,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { CheckIcon, Chevron"
},
{
"path": "apps/dokploy/components/dashboard/compose/general/generic/save-gitlab-provider-compose.tsx",
"chars": 15757,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { CheckIcon, Chevron"
},
{
"path": "apps/dokploy/components/dashboard/compose/general/generic/show.tsx",
"chars": 10157,
"preview": "import { CodeIcon, GitBranch, Loader2 } from \"lucide-react\";\nimport Link from \"next/link\";\nimport { useState } from \"rea"
},
{
"path": "apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx",
"chars": 5659,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { AlertTriangle } fr"
},
{
"path": "apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx",
"chars": 3172,
"preview": "import { Loader2, Puzzle, RefreshCw } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport { toast }"
},
{
"path": "apps/dokploy/components/dashboard/compose/general/show.tsx",
"chars": 1124,
"preview": "import { Badge } from \"@/components/ui/badge\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardHeader,\n\tCardTitle,\n"
},
{
"path": "apps/dokploy/components/dashboard/compose/logs/show-stack.tsx",
"chars": 4827,
"preview": "import { Loader2 } from \"lucide-react\";\nimport dynamic from \"next/dynamic\";\nimport { useEffect, useState } from \"react\";"
},
{
"path": "apps/dokploy/components/dashboard/compose/logs/show.tsx",
"chars": 2720,
"preview": "import { Loader2 } from \"lucide-react\";\nimport dynamic from \"next/dynamic\";\nimport { useEffect, useState } from \"react\";"
},
{
"path": "apps/dokploy/components/dashboard/compose/update-compose.tsx",
"chars": 4074,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport { PenBoxIcon } from "
},
{
"path": "apps/dokploy/components/dashboard/database/backups/handle-backup.tsx",
"chars": 21622,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport {\n\tCheckIcon,\n\tChevr"
},
{
"path": "apps/dokploy/components/dashboard/database/backups/restore-backup.tsx",
"chars": 23074,
"preview": "import { standardSchemaResolver as zodResolver } from \"@hookform/resolvers/standard-schema\";\nimport copy from \"copy-to-c"
},
{
"path": "apps/dokploy/components/dashboard/database/backups/show-backups.tsx",
"chars": 12769,
"preview": "import {\n\tClipboardList,\n\tDatabase,\n\tDatabaseBackup,\n\tPlay,\n\tTrash2,\n} from \"lucide-react\";\nimport Link from \"next/link\""
},
{
"path": "apps/dokploy/components/dashboard/deployments/show-deployments-table.tsx",
"chars": 17211,
"preview": "\"use client\";\n\nimport {\n\ttype ColumnFiltersState,\n\tflexRender,\n\tgetCoreRowModel,\n\tgetFilteredRowModel,\n\tgetPaginationRow"
},
{
"path": "apps/dokploy/components/dashboard/deployments/show-queue-table.tsx",
"chars": 6752,
"preview": "\"use client\";\n\nimport type { inferRouterOutputs } from \"@trpc/server\";\nimport Link from \"next/link\";\nimport { ArrowRight"
},
{
"path": "apps/dokploy/components/dashboard/docker/config/show-container-config.tsx",
"chars": 1440,
"preview": "import { CodeEditor } from \"@/components/shared/code-editor\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription,\n\tDia"
},
{
"path": "apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx",
"chars": 10944,
"preview": "import copy from \"copy-to-clipboard\";\nimport {\n\tCheck,\n\tCopy,\n\tDownload as DownloadIcon,\n\tLoader2,\n\tPause,\n\tPlay,\n} from"
},
{
"path": "apps/dokploy/components/dashboard/docker/logs/line-count-filter.tsx",
"chars": 5215,
"preview": "import { Command as CommandPrimitive } from \"cmdk\";\nimport debounce from \"lodash/debounce\";\nimport { CheckIcon, Hash } f"
},
{
"path": "apps/dokploy/components/dashboard/docker/logs/show-docker-modal-logs.tsx",
"chars": 1263,
"preview": "import dynamic from \"next/dynamic\";\nimport type React from \"react\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription"
},
{
"path": "apps/dokploy/components/dashboard/docker/logs/show-docker-modal-stack-logs.tsx",
"chars": 1267,
"preview": "import dynamic from \"next/dynamic\";\nimport type React from \"react\";\nimport {\n\tDialog,\n\tDialogContent,\n\tDialogDescription"
},
{
"path": "apps/dokploy/components/dashboard/docker/logs/since-logs-filter.tsx",
"chars": 2959,
"preview": "import { CheckIcon } from \"lucide-react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/compo"
},
{
"path": "apps/dokploy/components/dashboard/docker/logs/status-logs-filter.tsx",
"chars": 4301,
"preview": "import { CheckIcon } from \"lucide-react\";\nimport type React from \"react\";\nimport { Badge } from \"@/components/ui/badge\";"
},
{
"path": "apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx",
"chars": 3344,
"preview": "import { FancyAnsi } from \"fancy-ansi\";\nimport escapeRegExp from \"lodash/escapeRegExp\";\nimport { Badge } from \"@/compone"
},
{
"path": "apps/dokploy/components/dashboard/docker/logs/utils.ts",
"chars": 4357,
"preview": "export type LogType = \"error\" | \"warning\" | \"success\" | \"info\" | \"debug\";\nexport type LogVariant = \"red\" | \"yellow\" | \"g"
},
{
"path": "apps/dokploy/components/dashboard/docker/show/colums.tsx",
"chars": 3275,
"preview": "import type { ColumnDef } from \"@tanstack/react-table\";\nimport { ArrowUpDown, MoreHorizontal } from \"lucide-react\";\nimpo"
}
]
// ... and 1017 more files (download for full content)
About this extraction
This page contains the full source code of the Dokploy/dokploy GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1217 files (23.8 MB), approximately 4.0M tokens, and a symbol index with 654 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.