Compare commits

..

10 Commits

Author SHA1 Message Date
Mauricio Siu
178f4fbdf7 fix: update Docker API version constant to use DOKPLOY environment variable 2026-03-10 10:12:00 -06:00
Mauricio Siu
2c07a4b2e3 Bump version from v0.28.5 to v0.28.6 2026-03-10 10:02:53 -06:00
Mauricio Siu
75a797097b Merge pull request #3952 from jirkavrba/copy-webhook-url
feat(deployments): Add option to copy webhook url by clicking on it
2026-03-10 10:00:04 -06:00
Jiří Vrba
de201d0b0a Add aria-label to webhook URL badge 2026-03-09 10:00:08 +01:00
autofix-ci[bot]
6866e2b63a [autofix.ci] apply automated fixes 2026-03-09 08:49:06 +00:00
Jiří Vrba
3e4a1b92eb Code review fixes 2026-03-09 09:48:37 +01:00
Jiří Vrba
b9ca6ea9db Code review fixes 2026-03-09 09:38:00 +01:00
Jiří Vrba
f1d4543d5e Code review fixes 2026-03-09 09:33:30 +01:00
autofix-ci[bot]
d8c7c1eaf4 [autofix.ci] apply automated fixes 2026-03-09 08:28:35 +00:00
Jiří Vrba
4330d7bd99 feat(deployments): Add option to copy webhook url by clicking on it 2026-03-09 09:25:41 +01:00
5 changed files with 34 additions and 367 deletions

View File

@@ -62,16 +62,6 @@ pnpm install
cp apps/dokploy/.env.example apps/dokploy/.env
```
### Optional Docker Configuration
The following environment variables can be added to your `.env` file if you need custom Docker daemon configuration:
- **DOCKER_API_VERSION**: Specify which Docker API version to use (optional)
- **DOKPLOY_DOCKER_HOST**: Specify a custom Docker daemon host (optional)
- **DOKPLOY_DOCKER_PORT**: Specify a custom Docker daemon port (optional)
These variables are typically not needed for standard local development but can be useful if you need to connect to a remote Docker daemon or require a specific Docker API version.
## Requirements
- [Docker](/GUIDES.md#docker)
@@ -181,11 +171,6 @@ curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.39.1/pack-v0.
- **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.
### Pull Request Guidelines
- **Keep PRs small and focused.** Avoid very large PRs; prefer several smaller PRs (e.g., one template or one logical change per PR). This speeds up review and keeps the history clear.
- **Test before submitting.** Any PR that has not been tested by the contributor will be closed. This keeps the PR queue tidy and ensures that only contributions that have been verified by their authors are considered.
Thank you for your contribution!
## Templates

View File

@@ -6,346 +6,3 @@ npm run dev
```
open http://localhost:3000
```
## Environment Variables
The API server requires the following environment variables for configuration:
### Inngest Configuration
Required for the GET /jobs endpoint to list deployment jobs:
- **INNGEST_BASE_URL** - The base URL for the Inngest instance
- Self-hosted: `http://localhost:8288`
- Production: `https://dev-inngest.dokploy.com`
- **INNGEST_SIGNING_KEY** - The signing key for authenticating with Inngest
Optional configuration for filtering and pagination:
- **INNGEST_EVENTS_RECEIVED_AFTER** (optional) - An RFC3339 timestamp to filter events received after a specific date (e.g., `2024-01-01T00:00:00Z`). If unset, no date filter is applied.
- **INNGEST_JOBS_MAX_EVENTS** (optional) - Maximum number of events to fetch when listing jobs. Default is 100, maximum is 10000. Used for pagination with cursor.
### Lemon Squeezy Integration
- **LEMON_SQUEEZY_API_KEY** - API key for Lemon Squeezy integration
- **LEMON_SQUEEZY_STORE_ID** - Store ID for Lemon Squeezy integration
### Docker Configuration
Optional configuration for customizing Docker daemon connections:
- **DOCKER_API_VERSION** (optional) - Specifies which Docker API version to use when connecting to the Docker daemon. If not set, the Docker client uses the default API version.
- **DOKPLOY_DOCKER_HOST** (optional) - Specifies the Docker daemon host to connect to. If not set, uses the default Docker socket connection.
- **DOKPLOY_DOCKER_PORT** (optional) - Specifies the port for connecting to the Docker daemon. If not set, uses the default port.
These variables allow advanced users to customize how the Dokploy API server connects to Docker, which can be useful for connecting to remote Docker daemons or using specific API versions.
## API Endpoints
### GET /jobs
Lists deployment jobs (Inngest runs) for a specified server.
**Query Parameters:**
- `serverId` (required) - The ID of the server to list deployment jobs for
**Response:**
Returns an array of deployment job objects with the same shape as BullMQ queue jobs:
```json
[
{
"id": "string",
"name": "string",
"data": {},
"timestamp": 0,
"processedOn": 0,
"finishedOn": 0,
"failedReason": "string",
"state": "string"
}
]
```
**Error Responses:**
- `400` - serverId is not provided
- `503` - INNGEST_BASE_URL is not configured
- `200` - Empty array on other errors
This endpoint is used by the UI to display deployment queue information in the dashboard.
## Search Endpoints
The following search endpoints provide flexible querying capabilities with pagination support. All search endpoints respect member permissions, returning only resources the user has access to.
### application.search
Search applications across name, appName, description, repository, owner, and dockerImage fields.
**Query Parameters:**
- `q` (optional string) - General search term that searches across name, appName, description, repository, owner, and dockerImage
- `name` (optional string) - Filter by application name
- `appName` (optional string) - Filter by app name
- `description` (optional string) - Filter by description
- `repository` (optional string) - Filter by repository
- `owner` (optional string) - Filter by owner
- `dockerImage` (optional string) - Filter by Docker image
- `projectId` (optional string) - Filter by project ID
- `environmentId` (optional string) - Filter by environment ID
- `limit` (number, default 20, min 1, max 100) - Maximum number of results
- `offset` (number, default 0, min 0) - Pagination offset
**Response:**
```json
{
"items": [
{
"applicationId": "string",
"name": "string",
"appName": "string",
"description": "string",
"environmentId": "string",
"applicationStatus": "string",
"sourceType": "string",
"createdAt": "string"
}
],
"total": 0
}
```
### compose.search
Search compose services with filtering by name, appName, and description.
**Query Parameters:**
- `q` (optional string) - General search term across name, appName, description
- `name` (optional string) - Filter by name
- `appName` (optional string) - Filter by app name
- `description` (optional string) - Filter by description
- `projectId` (optional string) - Filter by project ID
- `environmentId` (optional string) - Filter by environment ID
- `limit` (number, default 20, min 1, max 100) - Maximum results
- `offset` (number, default 0, min 0) - Pagination offset
**Response:**
```json
{
"items": [
{
"composeId": "string",
"name": "string",
"appName": "string",
"description": "string",
"environmentId": "string",
"composeStatus": "string",
"sourceType": "string",
"createdAt": "string"
}
],
"total": 0
}
```
### environment.search
Search environments by name and description.
**Query Parameters:**
- `q` (optional string) - General search term across name and description
- `name` (optional string) - Filter by name
- `description` (optional string) - Filter by description
- `projectId` (optional string) - Filter by project ID
- `limit` (number, default 20, min 1, max 100) - Maximum results
- `offset` (number, default 0, min 0) - Pagination offset
**Response:**
```json
{
"items": [
{
"environmentId": "string",
"name": "string",
"description": "string",
"createdAt": "string",
"env": "string",
"projectId": "string",
"isDefault": true
}
],
"total": 0
}
```
### project.search
Search projects by name and description.
**Query Parameters:**
- `q` (optional string) - General search term across name and description
- `name` (optional string) - Filter by name
- `description` (optional string) - Filter by description
- `limit` (number, default 20, min 1, max 100) - Maximum results
- `offset` (number, default 0, min 0) - Pagination offset
**Response:**
```json
{
"items": [
{
"projectId": "string",
"name": "string",
"description": "string",
"createdAt": "string",
"organizationId": "string",
"env": "string"
}
],
"total": 0
}
```
### Database Service Search Endpoints
The following database services all share the same search interface:
- **postgres.search**
- **mysql.search**
- **mariadb.search**
- **mongo.search**
- **redis.search**
**Query Parameters:**
- `q` (optional string) - General search term across name, appName, description
- `name` (optional string) - Filter by name
- `appName` (optional string) - Filter by app name
- `description` (optional string) - Filter by description
- `projectId` (optional string) - Filter by project ID
- `environmentId` (optional string) - Filter by environment ID
- `limit` (number, default 20, min 1, max 100) - Maximum results
- `offset` (number, default 0, min 0) - Pagination offset
**Response:**
```json
{
"items": [
{
"postgresId": "string",
"name": "string",
"appName": "string",
"description": "string",
"environmentId": "string",
"applicationStatus": "string",
"createdAt": "string"
}
],
"total": 0
}
```
*Note: The response shape is similar across all database services, with the ID field varying (e.g., `mysqlId`, `mariadbId`, `mongoId`, `redisId`).*
**Search Behavior:**
- All searches use case-insensitive pattern matching with wildcards
- Results are ordered by creation date (descending)
- Members only see services they have access to
- Returns total count for pagination UI
## Whitelabeling Endpoints
The whitelabeling endpoints allow enterprise/self-hosted Dokploy instances to customize branding, logos, colors, and UI appearance. These endpoints are only available in self-hosted mode (not cloud).
### whitelabeling.get
Get the current whitelabeling configuration.
**Requirements:**
- Enterprise license required
- Only available for self-hosted (not cloud)
**Response:**
Returns the whitelabeling configuration object or null if not configured.
```json
{
"appName": "string | null",
"appDescription": "string | null",
"logoUrl": "string | null",
"faviconUrl": "string | null",
"primaryColor": "string | null",
"customCss": "string | null",
"loginLogoUrl": "string | null",
"supportUrl": "string | null",
"docsUrl": "string | null",
"errorPageTitle": "string | null",
"errorPageDescription": "string | null",
"metaTitle": "string | null",
"footerText": "string | null"
}
```
### whitelabeling.update
Update the whitelabeling configuration.
**Requirements:**
- Enterprise license required
- Owner role required
- Only available for self-hosted (not cloud)
**Input:**
```json
{
"whitelabelingConfig": {
"appName": "string | null",
"appDescription": "string | null",
"logoUrl": "string | null",
"faviconUrl": "string | null",
"primaryColor": "string | null",
"customCss": "string | null",
"loginLogoUrl": "string | null",
"supportUrl": "string | null",
"docsUrl": "string | null",
"errorPageTitle": "string | null",
"errorPageDescription": "string | null",
"metaTitle": "string | null",
"footerText": "string | null"
}
}
```
**Response:**
```json
{
"success": true
}
```
### whitelabeling.reset
Reset whitelabeling configuration to default values (all fields set to null).
**Requirements:**
- Enterprise license required
- Owner role required
- Only available for self-hosted (not cloud)
**Response:**
```json
{
"success": true
}
```
### whitelabeling.getPublic
Public endpoint to fetch whitelabeling configuration. This endpoint can be accessed without authentication, allowing the whitelabeling settings to be applied globally (including on the login page before auth).
**Requirements:**
- No authentication required
- Only available for self-hosted (not cloud)
**Response:**
Returns the whitelabeling configuration object or null if not configured. Response shape is identical to `whitelabeling.get`.

View File

@@ -2,6 +2,7 @@ import {
ChevronDown,
ChevronUp,
Clock,
Copy,
Loader2,
RefreshCcw,
RocketIcon,
@@ -10,6 +11,7 @@ import {
} from "lucide-react";
import React, { useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
import copy from "copy-to-clipboard";
import { AlertBlock } from "@/components/shared/alert-block";
import { DateTooltip } from "@/components/shared/date-tooltip";
import { DialogAction } from "@/components/shared/dialog-action";
@@ -97,6 +99,12 @@ export const ShowDeployments = ({
new Set(),
);
const webhookUrl = useMemo(
() =>
`${url}/api/deploy${type === "compose" ? "/compose" : ""}/${refreshToken}`,
[url, refreshToken, type],
);
const MAX_DESCRIPTION_LENGTH = 200;
const truncateDescription = (description: string): string => {
@@ -224,11 +232,27 @@ export const ShowDeployments = ({
<div className="flex flex-row items-center gap-2 flex-wrap">
<span>Webhook URL: </span>
<div className="flex flex-row items-center gap-2">
<span className="break-all text-muted-foreground">
{`${url}/api/deploy${
type === "compose" ? "/compose" : ""
}/${refreshToken}`}
</span>
<Badge
role="button"
tabIndex={0}
aria-label="Copy webhook URL to clipboard"
className="p-2 rounded-md ml-1 mr-1 hover:border-primary hover:text-primary-foreground hover:bg-primary hover:cursor-pointer whitespace-normal break-all"
variant="outline"
onKeyDown={(event) => {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault();
copy(webhookUrl);
toast.success("Copied to clipboard.");
}
}}
onClick={() => {
copy(webhookUrl);
toast.success("Copied to clipboard.");
}}
>
{webhookUrl}
<Copy className="h-4 w-4 ml-2" />
</Badge>
{(type === "application" || type === "compose") && (
<RefreshToken id={id} type={type} />
)}

View File

@@ -1,6 +1,6 @@
{
"name": "dokploy",
"version": "v0.28.5",
"version": "v0.28.6",
"private": true,
"license": "Apache-2.0",
"type": "module",

View File

@@ -2,7 +2,8 @@ import path from "node:path";
import Docker from "dockerode";
export const IS_CLOUD = process.env.IS_CLOUD === "true";
export const DOCKER_API_VERSION = process.env.DOCKER_API_VERSION;
export const DOKPLOY_DOCKER_API_VERSION =
process.env.DOKPLOY_DOCKER_API_VERSION;
export const DOKPLOY_DOCKER_HOST = process.env.DOKPLOY_DOCKER_HOST;
export const DOKPLOY_DOCKER_PORT = process.env.DOKPLOY_DOCKER_PORT
? Number(process.env.DOKPLOY_DOCKER_PORT)
@@ -10,8 +11,8 @@ export const DOKPLOY_DOCKER_PORT = process.env.DOKPLOY_DOCKER_PORT
export const CLEANUP_CRON_JOB = "50 23 * * *";
export const docker = new Docker({
...(DOCKER_API_VERSION && {
version: DOCKER_API_VERSION,
...(DOKPLOY_DOCKER_API_VERSION && {
version: DOKPLOY_DOCKER_API_VERSION,
}),
...(DOKPLOY_DOCKER_HOST && {
host: DOKPLOY_DOCKER_HOST,