From 64643c11aa994867fc4002afab8986ea87311719 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Sun, 9 Mar 2025 13:00:22 -0600
Subject: [PATCH] feat(ui): add Docker Compose file editor autocomplete
- Implement Docker Compose file autocompletion in CodeMirror
- Add comprehensive suggestions for top-level and service-level keys
- Include a JSON schema for Docker Compose file validation
- Enhance code editor with intelligent YAML editing support
---
.../compose/general/compose-file-editor.tsx | 1 +
.../dokploy/components/shared/code-editor.tsx | 116 ++
.../components/shared/compose-spec.json | 1078 +++++++++++++++++
apps/dokploy/package.json | 53 +-
pnpm-lock.yaml | 31 +-
5 files changed, 1244 insertions(+), 35 deletions(-)
create mode 100644 apps/dokploy/components/shared/compose-spec.json
diff --git a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx
index 72589582..bbcbfd83 100644
--- a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx
+++ b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx
@@ -97,6 +97,7 @@ export const ComposeFileEditor = ({ composeId }: Props) => {
({
+ ...opt,
+ apply: (view: EditorView, completion: Completion) => {
+ const insert = `${completion.label}:`;
+ view.dispatch({
+ changes: {
+ from: view.state.selection.main.from,
+ to: view.state.selection.main.to,
+ insert,
+ },
+ selection: { anchor: view.state.selection.main.from + insert.length },
+ });
+ },
+}));
+
+const dockerComposeServiceOptions = [
+ {
+ label: "image",
+ type: "keyword",
+ info: "Specify the image to start the container from",
+ },
+ { label: "build", type: "keyword", info: "Build configuration" },
+ { label: "command", type: "keyword", info: "Override the default command" },
+ { label: "container_name", type: "keyword", info: "Custom container name" },
+ {
+ label: "depends_on",
+ type: "keyword",
+ info: "Express dependency between services",
+ },
+ { label: "environment", type: "keyword", info: "Add environment variables" },
+ {
+ label: "env_file",
+ type: "keyword",
+ info: "Add environment variables from a file",
+ },
+ {
+ label: "expose",
+ type: "keyword",
+ info: "Expose ports without publishing them",
+ },
+ { label: "ports", type: "keyword", info: "Expose ports" },
+ {
+ label: "volumes",
+ type: "keyword",
+ info: "Mount host paths or named volumes",
+ },
+ { label: "restart", type: "keyword", info: "Restart policy" },
+ { label: "networks", type: "keyword", info: "Networks to join" },
+].map((opt) => ({
+ ...opt,
+ apply: (view: EditorView, completion: Completion) => {
+ const insert = `${completion.label}:`;
+ view.dispatch({
+ changes: {
+ from: view.state.selection.main.from,
+ to: view.state.selection.main.to,
+ insert,
+ },
+ selection: { anchor: view.state.selection.main.from + insert.length },
+ });
+ },
+}));
+
+function dockerComposeComplete(
+ context: CompletionContext,
+): CompletionResult | null {
+ const word = context.matchBefore(/\w*/);
+ if (!word) return null;
+
+ if (!word.text && !context.explicit) return null;
+
+ // Check if we're at the root level
+ const line = context.state.doc.lineAt(context.pos);
+ const indentation = /^\s*/.exec(line.text)?.[0].length || 0;
+
+ console.log(indentation);
+ if (indentation === 0) {
+ return {
+ from: word.from,
+ options: dockerComposeServices,
+ validFor: /^\w*$/,
+ };
+ }
+
+ // If we're inside a service definition
+ if (indentation === 4) {
+ return {
+ from: word.from,
+ options: dockerComposeServiceOptions,
+ validFor: /^\w*$/,
+ };
+ }
+
+ return null;
+}
+
interface Props extends ReactCodeMirrorProps {
wrapperClassName?: string;
disabled?: boolean;
@@ -45,6 +156,11 @@ export const CodeEditor = ({
? StreamLanguage.define(shell)
: StreamLanguage.define(properties),
props.lineWrapping ? EditorView.lineWrapping : [],
+ language === "yaml"
+ ? autocompletion({
+ override: [dockerComposeComplete],
+ })
+ : [],
]}
{...props}
editable={!props.disabled}
diff --git a/apps/dokploy/components/shared/compose-spec.json b/apps/dokploy/components/shared/compose-spec.json
new file mode 100644
index 00000000..92036daa
--- /dev/null
+++ b/apps/dokploy/components/shared/compose-spec.json
@@ -0,0 +1,1078 @@
+{
+ "$schema": "https://json-schema.org/draft/2019-09/schema#",
+ "id": "compose_spec.json",
+ "type": "object",
+ "title": "Compose Specification",
+ "description": "The Compose file is a YAML file defining a multi-containers based application.",
+
+ "properties": {
+ "version": {
+ "type": "string",
+ "description": "declared for backward compatibility, ignored."
+ },
+
+ "name": {
+ "type": "string",
+ "description": "define the Compose project name, until user defines one explicitly."
+ },
+
+ "include": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/include"
+ },
+ "description": "compose sub-projects to be included."
+ },
+
+ "services": {
+ "id": "#/properties/services",
+ "type": "object",
+ "patternProperties": {
+ "^[a-zA-Z0-9._-]+$": {
+ "$ref": "#/definitions/service"
+ }
+ },
+ "additionalProperties": false
+ },
+
+ "networks": {
+ "id": "#/properties/networks",
+ "type": "object",
+ "patternProperties": {
+ "^[a-zA-Z0-9._-]+$": {
+ "$ref": "#/definitions/network"
+ }
+ }
+ },
+
+ "volumes": {
+ "id": "#/properties/volumes",
+ "type": "object",
+ "patternProperties": {
+ "^[a-zA-Z0-9._-]+$": {
+ "$ref": "#/definitions/volume"
+ }
+ },
+ "additionalProperties": false
+ },
+
+ "secrets": {
+ "id": "#/properties/secrets",
+ "type": "object",
+ "patternProperties": {
+ "^[a-zA-Z0-9._-]+$": {
+ "$ref": "#/definitions/secret"
+ }
+ },
+ "additionalProperties": false
+ },
+
+ "configs": {
+ "id": "#/properties/configs",
+ "type": "object",
+ "patternProperties": {
+ "^[a-zA-Z0-9._-]+$": {
+ "$ref": "#/definitions/config"
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+
+ "patternProperties": { "^x-": {} },
+ "additionalProperties": false,
+
+ "definitions": {
+ "service": {
+ "id": "#/definitions/service",
+ "type": "object",
+
+ "properties": {
+ "develop": { "$ref": "#/definitions/development" },
+ "deploy": { "$ref": "#/definitions/deployment" },
+ "annotations": { "$ref": "#/definitions/list_or_dict" },
+ "attach": { "type": ["boolean", "string"] },
+ "build": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "properties": {
+ "context": { "type": "string" },
+ "dockerfile": { "type": "string" },
+ "dockerfile_inline": { "type": "string" },
+ "entitlements": {
+ "type": "array",
+ "items": { "type": "string" }
+ },
+ "args": { "$ref": "#/definitions/list_or_dict" },
+ "ssh": { "$ref": "#/definitions/list_or_dict" },
+ "labels": { "$ref": "#/definitions/list_or_dict" },
+ "cache_from": {
+ "type": "array",
+ "items": { "type": "string" }
+ },
+ "cache_to": { "type": "array", "items": { "type": "string" } },
+ "no_cache": { "type": ["boolean", "string"] },
+ "additional_contexts": { "$ref": "#/definitions/list_or_dict" },
+ "network": { "type": "string" },
+ "pull": { "type": ["boolean", "string"] },
+ "target": { "type": "string" },
+ "shm_size": { "type": ["integer", "string"] },
+ "extra_hosts": { "$ref": "#/definitions/extra_hosts" },
+ "isolation": { "type": "string" },
+ "privileged": { "type": ["boolean", "string"] },
+ "secrets": { "$ref": "#/definitions/service_config_or_secret" },
+ "tags": { "type": "array", "items": { "type": "string" } },
+ "ulimits": { "$ref": "#/definitions/ulimits" },
+ "platforms": { "type": "array", "items": { "type": "string" } }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ ]
+ },
+ "blkio_config": {
+ "type": "object",
+ "properties": {
+ "device_read_bps": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/blkio_limit" }
+ },
+ "device_read_iops": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/blkio_limit" }
+ },
+ "device_write_bps": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/blkio_limit" }
+ },
+ "device_write_iops": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/blkio_limit" }
+ },
+ "weight": { "type": ["integer", "string"] },
+ "weight_device": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/blkio_weight" }
+ }
+ },
+ "additionalProperties": false
+ },
+ "cap_add": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true
+ },
+ "cap_drop": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true
+ },
+ "cgroup": { "type": "string", "enum": ["host", "private"] },
+ "cgroup_parent": { "type": "string" },
+ "command": { "$ref": "#/definitions/command" },
+ "configs": { "$ref": "#/definitions/service_config_or_secret" },
+ "container_name": { "type": "string" },
+ "cpu_count": {
+ "oneOf": [{ "type": "string" }, { "type": "integer", "minimum": 0 }]
+ },
+ "cpu_percent": {
+ "oneOf": [
+ { "type": "string" },
+ { "type": "integer", "minimum": 0, "maximum": 100 }
+ ]
+ },
+ "cpu_shares": { "type": ["number", "string"] },
+ "cpu_quota": { "type": ["number", "string"] },
+ "cpu_period": { "type": ["number", "string"] },
+ "cpu_rt_period": { "type": ["number", "string"] },
+ "cpu_rt_runtime": { "type": ["number", "string"] },
+ "cpus": { "type": ["number", "string"] },
+ "cpuset": { "type": "string" },
+ "credential_spec": {
+ "type": "object",
+ "properties": {
+ "config": { "type": "string" },
+ "file": { "type": "string" },
+ "registry": { "type": "string" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "depends_on": {
+ "oneOf": [
+ { "$ref": "#/definitions/list_of_strings" },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "patternProperties": {
+ "^[a-zA-Z0-9._-]+$": {
+ "type": "object",
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} },
+ "properties": {
+ "restart": { "type": ["boolean", "string"] },
+ "required": {
+ "type": "boolean",
+ "default": true
+ },
+ "condition": {
+ "type": "string",
+ "enum": [
+ "service_started",
+ "service_healthy",
+ "service_completed_successfully"
+ ]
+ }
+ },
+ "required": ["condition"]
+ }
+ }
+ }
+ ]
+ },
+ "device_cgroup_rules": { "$ref": "#/definitions/list_of_strings" },
+ "devices": {
+ "type": "array",
+ "items": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "required": ["source"],
+ "properties": {
+ "source": { "type": "string" },
+ "target": { "type": "string" },
+ "permissions": { "type": "string" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ ]
+ }
+ },
+ "dns": { "$ref": "#/definitions/string_or_list" },
+ "dns_opt": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true
+ },
+ "dns_search": { "$ref": "#/definitions/string_or_list" },
+ "domainname": { "type": "string" },
+ "entrypoint": { "$ref": "#/definitions/command" },
+ "env_file": { "$ref": "#/definitions/env_file" },
+ "label_file": { "$ref": "#/definitions/label_file" },
+ "environment": { "$ref": "#/definitions/list_or_dict" },
+
+ "expose": {
+ "type": "array",
+ "items": {
+ "type": ["string", "number"],
+ "format": "expose"
+ },
+ "uniqueItems": true
+ },
+ "extends": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+
+ "properties": {
+ "service": { "type": "string" },
+ "file": { "type": "string" }
+ },
+ "required": ["service"],
+ "additionalProperties": false
+ }
+ ]
+ },
+ "external_links": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true
+ },
+ "extra_hosts": { "$ref": "#/definitions/extra_hosts" },
+ "gpus": { "$ref": "#/definitions/gpus" },
+ "group_add": {
+ "type": "array",
+ "items": {
+ "type": ["string", "number"]
+ },
+ "uniqueItems": true
+ },
+ "healthcheck": { "$ref": "#/definitions/healthcheck" },
+ "hostname": { "type": "string" },
+ "image": { "type": "string" },
+ "init": { "type": ["boolean", "string"] },
+ "ipc": { "type": "string" },
+ "isolation": { "type": "string" },
+ "labels": { "$ref": "#/definitions/list_or_dict" },
+ "links": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true
+ },
+ "logging": {
+ "type": "object",
+
+ "properties": {
+ "driver": { "type": "string" },
+ "options": {
+ "type": "object",
+ "patternProperties": {
+ "^.+$": { "type": ["string", "number", "null"] }
+ }
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "mac_address": { "type": "string" },
+ "mem_limit": { "type": ["number", "string"] },
+ "mem_reservation": { "type": ["string", "integer"] },
+ "mem_swappiness": { "type": ["integer", "string"] },
+ "memswap_limit": { "type": ["number", "string"] },
+ "network_mode": { "type": "string" },
+ "networks": {
+ "oneOf": [
+ { "$ref": "#/definitions/list_of_strings" },
+ {
+ "type": "object",
+ "patternProperties": {
+ "^[a-zA-Z0-9._-]+$": {
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "aliases": { "$ref": "#/definitions/list_of_strings" },
+ "ipv4_address": { "type": "string" },
+ "ipv6_address": { "type": "string" },
+ "link_local_ips": {
+ "$ref": "#/definitions/list_of_strings"
+ },
+ "mac_address": { "type": "string" },
+ "driver_opts": {
+ "type": "object",
+ "patternProperties": {
+ "^.+$": { "type": ["string", "number"] }
+ }
+ },
+ "priority": { "type": "number" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ { "type": "null" }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ ]
+ },
+ "oom_kill_disable": { "type": ["boolean", "string"] },
+ "oom_score_adj": {
+ "oneOf": [
+ { "type": "string" },
+ { "type": "integer", "minimum": -1000, "maximum": 1000 }
+ ]
+ },
+ "pid": { "type": ["string", "null"] },
+ "pids_limit": { "type": ["number", "string"] },
+ "platform": { "type": "string" },
+ "ports": {
+ "type": "array",
+ "items": {
+ "oneOf": [
+ { "type": "number" },
+ { "type": "string" },
+ {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "mode": { "type": "string" },
+ "host_ip": { "type": "string" },
+ "target": { "type": ["integer", "string"] },
+ "published": { "type": ["string", "integer"] },
+ "protocol": { "type": "string" },
+ "app_protocol": { "type": "string" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ ]
+ },
+ "uniqueItems": true
+ },
+ "post_start": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/service_hook" }
+ },
+ "pre_stop": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/service_hook" }
+ },
+ "privileged": { "type": ["boolean", "string"] },
+ "profiles": { "$ref": "#/definitions/list_of_strings" },
+ "pull_policy": {
+ "type": "string",
+ "enum": ["always", "never", "if_not_present", "build", "missing"]
+ },
+ "read_only": { "type": ["boolean", "string"] },
+ "restart": { "type": "string" },
+ "runtime": {
+ "type": "string"
+ },
+ "scale": {
+ "type": ["integer", "string"]
+ },
+ "security_opt": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true
+ },
+ "shm_size": { "type": ["number", "string"] },
+ "secrets": { "$ref": "#/definitions/service_config_or_secret" },
+ "sysctls": { "$ref": "#/definitions/list_or_dict" },
+ "stdin_open": { "type": ["boolean", "string"] },
+ "stop_grace_period": { "type": "string" },
+ "stop_signal": { "type": "string" },
+ "storage_opt": { "type": "object" },
+ "tmpfs": { "$ref": "#/definitions/string_or_list" },
+ "tty": { "type": ["boolean", "string"] },
+ "ulimits": { "$ref": "#/definitions/ulimits" },
+ "user": { "type": "string" },
+ "uts": { "type": "string" },
+ "userns_mode": { "type": "string" },
+ "volumes": {
+ "type": "array",
+ "items": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "required": ["type"],
+ "properties": {
+ "type": { "type": "string" },
+ "source": { "type": "string" },
+ "target": { "type": "string" },
+ "read_only": { "type": ["boolean", "string"] },
+ "consistency": { "type": "string" },
+ "bind": {
+ "type": "object",
+ "properties": {
+ "propagation": { "type": "string" },
+ "create_host_path": { "type": ["boolean", "string"] },
+ "recursive": {
+ "type": "string",
+ "enum": ["enabled", "disabled", "writable", "readonly"]
+ },
+ "selinux": { "type": "string", "enum": ["z", "Z"] }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "volume": {
+ "type": "object",
+ "properties": {
+ "nocopy": { "type": ["boolean", "string"] },
+ "subpath": { "type": "string" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "tmpfs": {
+ "type": "object",
+ "properties": {
+ "size": {
+ "oneOf": [
+ { "type": "integer", "minimum": 0 },
+ { "type": "string" }
+ ]
+ },
+ "mode": { "type": ["number", "string"] }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ ]
+ },
+ "uniqueItems": true
+ },
+ "volumes_from": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true
+ },
+ "working_dir": { "type": "string" }
+ },
+ "patternProperties": { "^x-": {} },
+ "additionalProperties": false
+ },
+
+ "healthcheck": {
+ "id": "#/definitions/healthcheck",
+ "type": "object",
+ "properties": {
+ "disable": { "type": ["boolean", "string"] },
+ "interval": { "type": "string" },
+ "retries": { "type": ["number", "string"] },
+ "test": {
+ "oneOf": [
+ { "type": "string" },
+ { "type": "array", "items": { "type": "string" } }
+ ]
+ },
+ "timeout": { "type": "string" },
+ "start_period": { "type": "string" },
+ "start_interval": { "type": "string" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "development": {
+ "id": "#/definitions/development",
+ "type": ["object", "null"],
+ "properties": {
+ "watch": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": ["path", "action"],
+ "properties": {
+ "ignore": { "type": "array", "items": { "type": "string" } },
+ "path": { "type": "string" },
+ "action": {
+ "type": "string",
+ "enum": [
+ "rebuild",
+ "sync",
+ "restart",
+ "sync+restart",
+ "sync+exec"
+ ]
+ },
+ "target": { "type": "string" },
+ "exec": { "$ref": "#/definitions/service_hook" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "deployment": {
+ "id": "#/definitions/deployment",
+ "type": ["object", "null"],
+ "properties": {
+ "mode": { "type": "string" },
+ "endpoint_mode": { "type": "string" },
+ "replicas": { "type": ["integer", "string"] },
+ "labels": { "$ref": "#/definitions/list_or_dict" },
+ "rollback_config": {
+ "type": "object",
+ "properties": {
+ "parallelism": { "type": ["integer", "string"] },
+ "delay": { "type": "string" },
+ "failure_action": { "type": "string" },
+ "monitor": { "type": "string" },
+ "max_failure_ratio": { "type": ["number", "string"] },
+ "order": { "type": "string", "enum": ["start-first", "stop-first"] }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "update_config": {
+ "type": "object",
+ "properties": {
+ "parallelism": { "type": ["integer", "string"] },
+ "delay": { "type": "string" },
+ "failure_action": { "type": "string" },
+ "monitor": { "type": "string" },
+ "max_failure_ratio": { "type": ["number", "string"] },
+ "order": { "type": "string", "enum": ["start-first", "stop-first"] }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "resources": {
+ "type": "object",
+ "properties": {
+ "limits": {
+ "type": "object",
+ "properties": {
+ "cpus": { "type": ["number", "string"] },
+ "memory": { "type": "string" },
+ "pids": { "type": ["integer", "string"] }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "reservations": {
+ "type": "object",
+ "properties": {
+ "cpus": { "type": ["number", "string"] },
+ "memory": { "type": "string" },
+ "generic_resources": {
+ "$ref": "#/definitions/generic_resources"
+ },
+ "devices": { "$ref": "#/definitions/devices" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "restart_policy": {
+ "type": "object",
+ "properties": {
+ "condition": { "type": "string" },
+ "delay": { "type": "string" },
+ "max_attempts": { "type": ["integer", "string"] },
+ "window": { "type": "string" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "placement": {
+ "type": "object",
+ "properties": {
+ "constraints": { "type": "array", "items": { "type": "string" } },
+ "preferences": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "spread": { "type": "string" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ },
+ "max_replicas_per_node": { "type": ["integer", "string"] }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+
+ "generic_resources": {
+ "id": "#/definitions/generic_resources",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "discrete_resource_spec": {
+ "type": "object",
+ "properties": {
+ "kind": { "type": "string" },
+ "value": { "type": ["number", "string"] }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ },
+
+ "devices": {
+ "id": "#/definitions/devices",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "capabilities": { "$ref": "#/definitions/list_of_strings" },
+ "count": { "type": ["string", "integer"] },
+ "device_ids": { "$ref": "#/definitions/list_of_strings" },
+ "driver": { "type": "string" },
+ "options": { "$ref": "#/definitions/list_or_dict" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} },
+ "required": ["capabilities"]
+ }
+ },
+
+ "gpus": {
+ "id": "#/definitions/gpus",
+ "oneOf": [
+ { "type": "string", "enum": ["all"] },
+ {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "capabilities": { "$ref": "#/definitions/list_of_strings" },
+ "count": { "type": ["string", "integer"] },
+ "device_ids": { "$ref": "#/definitions/list_of_strings" },
+ "driver": { "type": "string" },
+ "options": { "$ref": "#/definitions/list_or_dict" }
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ ]
+ },
+
+ "include": {
+ "id": "#/definitions/include",
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "properties": {
+ "path": { "$ref": "#/definitions/string_or_list" },
+ "env_file": { "$ref": "#/definitions/string_or_list" },
+ "project_directory": { "type": "string" }
+ },
+ "additionalProperties": false
+ }
+ ]
+ },
+
+ "network": {
+ "id": "#/definitions/network",
+ "type": ["object", "null"],
+ "properties": {
+ "name": { "type": "string" },
+ "driver": { "type": "string" },
+ "driver_opts": {
+ "type": "object",
+ "patternProperties": {
+ "^.+$": { "type": ["string", "number"] }
+ }
+ },
+ "ipam": {
+ "type": "object",
+ "properties": {
+ "driver": { "type": "string" },
+ "config": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "subnet": { "type": "string" },
+ "ip_range": { "type": "string" },
+ "gateway": { "type": "string" },
+ "aux_addresses": {
+ "type": "object",
+ "additionalProperties": false,
+ "patternProperties": { "^.+$": { "type": "string" } }
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ },
+ "options": {
+ "type": "object",
+ "additionalProperties": false,
+ "patternProperties": { "^.+$": { "type": "string" } }
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "external": {
+ "type": ["boolean", "string", "object"],
+ "properties": {
+ "name": {
+ "deprecated": true,
+ "type": "string"
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "internal": { "type": ["boolean", "string"] },
+ "enable_ipv4": { "type": ["boolean", "string"] },
+ "enable_ipv6": { "type": ["boolean", "string"] },
+ "attachable": { "type": ["boolean", "string"] },
+ "labels": { "$ref": "#/definitions/list_or_dict" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+
+ "volume": {
+ "id": "#/definitions/volume",
+ "type": ["object", "null"],
+ "properties": {
+ "name": { "type": "string" },
+ "driver": { "type": "string" },
+ "driver_opts": {
+ "type": "object",
+ "patternProperties": {
+ "^.+$": { "type": ["string", "number"] }
+ }
+ },
+ "external": {
+ "type": ["boolean", "string", "object"],
+ "properties": {
+ "name": {
+ "deprecated": true,
+ "type": "string"
+ }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+ "labels": { "$ref": "#/definitions/list_or_dict" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+
+ "secret": {
+ "id": "#/definitions/secret",
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "environment": { "type": "string" },
+ "file": { "type": "string" },
+ "external": {
+ "type": ["boolean", "string", "object"],
+ "properties": {
+ "name": { "type": "string" }
+ }
+ },
+ "labels": { "$ref": "#/definitions/list_or_dict" },
+ "driver": { "type": "string" },
+ "driver_opts": {
+ "type": "object",
+ "patternProperties": {
+ "^.+$": { "type": ["string", "number"] }
+ }
+ },
+ "template_driver": { "type": "string" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+
+ "config": {
+ "id": "#/definitions/config",
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "content": { "type": "string" },
+ "environment": { "type": "string" },
+ "file": { "type": "string" },
+ "external": {
+ "type": ["boolean", "string", "object"],
+ "properties": {
+ "name": {
+ "deprecated": true,
+ "type": "string"
+ }
+ }
+ },
+ "labels": { "$ref": "#/definitions/list_or_dict" },
+ "template_driver": { "type": "string" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+
+ "command": {
+ "oneOf": [
+ { "type": "null" },
+ { "type": "string" },
+ { "type": "array", "items": { "type": "string" } }
+ ]
+ },
+
+ "service_hook": {
+ "id": "#/definitions/service_hook",
+ "type": "object",
+ "properties": {
+ "command": { "$ref": "#/definitions/command" },
+ "user": { "type": "string" },
+ "privileged": { "type": ["boolean", "string"] },
+ "working_dir": { "type": "string" },
+ "environment": { "$ref": "#/definitions/list_or_dict" }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ },
+
+ "env_file": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "array",
+ "items": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "format": {
+ "type": "string"
+ },
+ "required": {
+ "type": ["boolean", "string"],
+ "default": true
+ }
+ },
+ "required": ["path"]
+ }
+ ]
+ }
+ }
+ ]
+ },
+
+ "label_file": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "array",
+ "items": { "type": "string" }
+ }
+ ]
+ },
+
+ "string_or_list": {
+ "oneOf": [
+ { "type": "string" },
+ { "$ref": "#/definitions/list_of_strings" }
+ ]
+ },
+
+ "list_of_strings": {
+ "type": "array",
+ "items": { "type": "string" },
+ "uniqueItems": true
+ },
+
+ "list_or_dict": {
+ "oneOf": [
+ {
+ "type": "object",
+ "patternProperties": {
+ ".+": {
+ "type": ["string", "number", "boolean", "null"]
+ }
+ },
+ "additionalProperties": false
+ },
+ { "type": "array", "items": { "type": "string" }, "uniqueItems": true }
+ ]
+ },
+
+ "extra_hosts": {
+ "oneOf": [
+ {
+ "type": "object",
+ "patternProperties": {
+ ".+": {
+ "oneOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "uniqueItems": false
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ { "type": "array", "items": { "type": "string" }, "uniqueItems": true }
+ ]
+ },
+
+ "blkio_limit": {
+ "type": "object",
+ "properties": {
+ "path": { "type": "string" },
+ "rate": { "type": ["integer", "string"] }
+ },
+ "additionalProperties": false
+ },
+ "blkio_weight": {
+ "type": "object",
+ "properties": {
+ "path": { "type": "string" },
+ "weight": { "type": ["integer", "string"] }
+ },
+ "additionalProperties": false
+ },
+ "service_config_or_secret": {
+ "type": "array",
+ "items": {
+ "oneOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "properties": {
+ "source": { "type": "string" },
+ "target": { "type": "string" },
+ "uid": { "type": "string" },
+ "gid": { "type": "string" },
+ "mode": { "type": ["number", "string"] }
+ },
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ ]
+ }
+ },
+ "ulimits": {
+ "type": "object",
+ "patternProperties": {
+ "^[a-z]+$": {
+ "oneOf": [
+ { "type": ["integer", "string"] },
+ {
+ "type": "object",
+ "properties": {
+ "hard": { "type": ["integer", "string"] },
+ "soft": { "type": ["integer", "string"] }
+ },
+ "required": ["soft", "hard"],
+ "additionalProperties": false,
+ "patternProperties": { "^x-": {} }
+ }
+ ]
+ }
+ }
+ },
+ "constraints": {
+ "service": {
+ "id": "#/definitions/constraints/service",
+ "anyOf": [{ "required": ["build"] }, { "required": ["image"] }],
+ "properties": {
+ "build": {
+ "required": ["context"]
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json
index 5eab6fa8..7cc9ebf3 100644
--- a/apps/dokploy/package.json
+++ b/apps/dokploy/package.json
@@ -36,8 +36,6 @@
"test": "vitest --config __test__/vitest.config.ts"
},
"dependencies": {
- "micromatch": "4.0.8",
- "ai": "^4.0.23",
"@ai-sdk/anthropic": "^1.0.6",
"@ai-sdk/azure": "^1.0.15",
"@ai-sdk/cohere": "^1.0.6",
@@ -45,20 +43,7 @@
"@ai-sdk/mistral": "^1.0.6",
"@ai-sdk/openai": "^1.0.12",
"@ai-sdk/openai-compatible": "^0.0.13",
- "ollama-ai-provider": "^1.1.0",
- "better-auth": "1.2.0",
- "bl": "6.0.11",
- "rotating-file-stream": "3.2.3",
- "qrcode": "^1.5.3",
- "otpauth": "^9.2.3",
- "hi-base32": "^0.5.1",
- "boxen": "^7.1.1",
- "@octokit/auth-app": "^6.0.4",
- "nodemailer": "6.9.14",
- "@react-email/components": "^0.0.21",
- "node-os-utils": "1.3.7",
- "@lucia-auth/adapter-drizzle": "1.0.7",
- "dockerode": "4.0.2",
+ "@codemirror/autocomplete": "^6.18.6",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-yaml": "^6.1.1",
"@codemirror/language": "^6.10.1",
@@ -66,7 +51,10 @@
"@codemirror/view": "6.29.0",
"@dokploy/server": "workspace:*",
"@dokploy/trpc-openapi": "0.0.4",
+ "@faker-js/faker": "^8.4.1",
"@hookform/resolvers": "^3.9.0",
+ "@lucia-auth/adapter-drizzle": "1.0.7",
+ "@octokit/auth-app": "^6.0.4",
"@octokit/webhooks": "^13.2.7",
"@radix-ui/react-accordion": "1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
@@ -87,8 +75,10 @@
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.7",
+ "@react-email/components": "^0.0.21",
"@stepperize/react": "4.0.1",
"@stripe/stripe-js": "4.8.0",
+ "@tailwindcss/typography": "0.5.16",
"@tanstack/react-query": "^4.36.1",
"@tanstack/react-table": "^8.16.0",
"@trpc/client": "^10.43.6",
@@ -98,10 +88,14 @@
"@uiw/codemirror-theme-github": "^4.22.1",
"@uiw/react-codemirror": "^4.22.1",
"@xterm/addon-attach": "0.10.0",
- "@xterm/xterm": "^5.4.0",
"@xterm/addon-clipboard": "0.1.0",
+ "@xterm/xterm": "^5.4.0",
"adm-zip": "^0.5.14",
+ "ai": "^4.0.23",
"bcrypt": "5.1.1",
+ "better-auth": "1.2.0",
+ "bl": "6.0.11",
+ "boxen": "^7.1.1",
"bullmq": "5.4.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
@@ -109,10 +103,12 @@
"copy-to-clipboard": "^3.3.3",
"copy-webpack-plugin": "^12.0.2",
"date-fns": "3.6.0",
+ "dockerode": "4.0.2",
"dotenv": "16.4.5",
"drizzle-orm": "^0.39.1",
"drizzle-zod": "0.5.1",
"fancy-ansi": "^0.1.3",
+ "hi-base32": "^0.5.1",
"i18next": "^23.16.4",
"input-otp": "^1.2.4",
"js-cookie": "^3.0.5",
@@ -120,15 +116,21 @@
"lodash": "4.17.21",
"lucia": "^3.0.1",
"lucide-react": "^0.469.0",
+ "micromatch": "4.0.8",
"nanoid": "3",
"next": "^15.0.1",
"next-i18next": "^15.3.1",
"next-themes": "^0.2.1",
+ "node-os-utils": "1.3.7",
"node-pty": "1.0.0",
"node-schedule": "2.1.1",
+ "nodemailer": "6.9.14",
"octokit": "3.1.2",
+ "ollama-ai-provider": "^1.1.0",
+ "otpauth": "^9.2.3",
"postgres": "3.4.4",
"public-ip": "6.0.2",
+ "qrcode": "^1.5.3",
"react": "18.2.0",
"react-confetti-explosion": "2.1.2",
"react-day-picker": "8.10.1",
@@ -137,6 +139,7 @@
"react-i18next": "^15.1.0",
"react-markdown": "^9.0.1",
"recharts": "^2.12.7",
+ "rotating-file-stream": "3.2.3",
"slugify": "^1.6.6",
"sonner": "^1.5.0",
"ssh2": "1.15.0",
@@ -150,22 +153,20 @@
"ws": "8.16.0",
"xterm-addon-fit": "^0.8.0",
"zod": "^3.23.4",
- "zod-form-data": "^2.0.2",
- "@faker-js/faker": "^8.4.1",
- "@tailwindcss/typography": "0.5.16"
+ "zod-form-data": "^2.0.2"
},
"devDependencies": {
- "@types/micromatch": "4.0.9",
- "@types/qrcode": "^1.5.5",
- "@types/nodemailer": "^6.4.15",
- "@types/node-os-utils": "1.3.4",
"@types/adm-zip": "^0.5.5",
"@types/bcrypt": "5.0.2",
"@types/js-cookie": "^3.0.6",
"@types/js-yaml": "4.0.9",
"@types/lodash": "4.17.4",
+ "@types/micromatch": "4.0.9",
"@types/node": "^18.17.0",
+ "@types/node-os-utils": "1.3.4",
"@types/node-schedule": "2.1.6",
+ "@types/nodemailer": "^6.4.15",
+ "@types/qrcode": "^1.5.5",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@types/ssh2": "1.15.1",
@@ -196,6 +197,8 @@
]
},
"commitlint": {
- "extends": ["@commitlint/config-conventional"]
+ "extends": [
+ "@commitlint/config-conventional"
+ ]
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8d921aeb..ca809b5c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -118,12 +118,15 @@ importers:
'@ai-sdk/openai-compatible':
specifier: ^0.0.13
version: 0.0.13(zod@3.23.8)
+ '@codemirror/autocomplete':
+ specifier: ^6.18.6
+ version: 6.18.6
'@codemirror/lang-json':
specifier: ^6.0.1
version: 6.0.1
'@codemirror/lang-yaml':
specifier: ^6.1.1
- version: 6.1.1(@codemirror/view@6.29.0)
+ version: 6.1.1
'@codemirror/language':
specifier: ^6.10.1
version: 6.10.2
@@ -246,7 +249,7 @@ importers:
version: 4.23.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.29.0)
'@uiw/react-codemirror':
specifier: ^4.22.1
- version: 4.23.0(@babel/runtime@7.25.0)(@codemirror/autocomplete@6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.29.0)(@lezer/common@1.2.1))(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.29.0)(codemirror@6.0.1(@lezer/common@1.2.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
+ version: 4.23.0(@babel/runtime@7.25.0)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.29.0)(codemirror@6.0.1(@lezer/common@1.2.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@xterm/addon-attach':
specifier: 0.10.0
version: 0.10.0(@xterm/xterm@5.5.0)
@@ -997,6 +1000,9 @@ packages:
'@codemirror/view': ^6.0.0
'@lezer/common': ^1.0.0
+ '@codemirror/autocomplete@6.18.6':
+ resolution: {integrity: sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==}
+
'@codemirror/commands@6.6.0':
resolution: {integrity: sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==}
@@ -8018,6 +8024,13 @@ snapshots:
'@codemirror/view': 6.29.0
'@lezer/common': 1.2.1
+ '@codemirror/autocomplete@6.18.6':
+ dependencies:
+ '@codemirror/language': 6.10.2
+ '@codemirror/state': 6.4.1
+ '@codemirror/view': 6.29.0
+ '@lezer/common': 1.2.1
+
'@codemirror/commands@6.6.0':
dependencies:
'@codemirror/language': 6.10.2
@@ -8030,16 +8043,14 @@ snapshots:
'@codemirror/language': 6.10.2
'@lezer/json': 1.0.2
- '@codemirror/lang-yaml@6.1.1(@codemirror/view@6.29.0)':
+ '@codemirror/lang-yaml@6.1.1':
dependencies:
- '@codemirror/autocomplete': 6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.29.0)(@lezer/common@1.2.1)
+ '@codemirror/autocomplete': 6.18.6
'@codemirror/language': 6.10.2
'@codemirror/state': 6.4.1
'@lezer/common': 1.2.1
'@lezer/highlight': 1.2.0
'@lezer/yaml': 1.0.3
- transitivePeerDependencies:
- - '@codemirror/view'
'@codemirror/language@6.10.2':
dependencies:
@@ -10808,9 +10819,9 @@ snapshots:
dependencies:
'@types/node': 20.14.10
- '@uiw/codemirror-extensions-basic-setup@4.23.0(@codemirror/autocomplete@6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.29.0)(@lezer/common@1.2.1))(@codemirror/commands@6.6.0)(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.29.0)':
+ '@uiw/codemirror-extensions-basic-setup@4.23.0(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.6.0)(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.29.0)':
dependencies:
- '@codemirror/autocomplete': 6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.29.0)(@lezer/common@1.2.1)
+ '@codemirror/autocomplete': 6.18.6
'@codemirror/commands': 6.6.0
'@codemirror/language': 6.10.2
'@codemirror/lint': 6.8.1
@@ -10832,14 +10843,14 @@ snapshots:
'@codemirror/state': 6.4.1
'@codemirror/view': 6.29.0
- '@uiw/react-codemirror@4.23.0(@babel/runtime@7.25.0)(@codemirror/autocomplete@6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.29.0)(@lezer/common@1.2.1))(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.29.0)(codemirror@6.0.1(@lezer/common@1.2.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
+ '@uiw/react-codemirror@4.23.0(@babel/runtime@7.25.0)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.29.0)(codemirror@6.0.1(@lezer/common@1.2.1))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@babel/runtime': 7.25.0
'@codemirror/commands': 6.6.0
'@codemirror/state': 6.4.1
'@codemirror/theme-one-dark': 6.1.2
'@codemirror/view': 6.29.0
- '@uiw/codemirror-extensions-basic-setup': 4.23.0(@codemirror/autocomplete@6.17.0(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.29.0)(@lezer/common@1.2.1))(@codemirror/commands@6.6.0)(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.29.0)
+ '@uiw/codemirror-extensions-basic-setup': 4.23.0(@codemirror/autocomplete@6.18.6)(@codemirror/commands@6.6.0)(@codemirror/language@6.10.2)(@codemirror/lint@6.8.1)(@codemirror/search@6.5.6)(@codemirror/state@6.4.1)(@codemirror/view@6.29.0)
codemirror: 6.0.1(@lezer/common@1.2.1)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)