diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..16e8e6664 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["biomejs.biome"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..99357f236 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "source.fixAll.biome": "explicit", + "source.organizeImports.biome": "explicit" + } +} diff --git a/Dockerfile b/Dockerfile index 4d18a99ab..11310b18e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -58,7 +58,7 @@ RUN curl -sSL https://nixpacks.com/install.sh -o install.sh \ && pnpm install -g tsx # Install Railpack -ARG RAILPACK_VERSION=0.0.64 +ARG RAILPACK_VERSION=0.2.2 RUN curl -sSL https://railpack.com/install.sh | bash # Install buildpacks diff --git a/README.md b/README.md index bd27474e0..fb2ee82a5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
+
@@ -13,7 +13,7 @@
Dokploy is a free, self-hostable Platform as a Service (PaaS) that simplifies the deployment and management of applications and databases.
-### Features
+## ✨ Features
Dokploy includes multiple features to make your life easier.
@@ -43,7 +43,7 @@ curl -sSL https://dokploy.com/install.sh | sh
For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
-## Sponsors
+## ♥️ Sponsors
🙏 We're deeply grateful to all our sponsors who make Dokploy possible! Your support helps cover the costs of hosting, testing, and developing new features.
@@ -95,7 +95,6 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
### Community Backers 🤝
-
#### Organizations:
[Sponsors on Open Collective](https://opencollective.com/dokploy)
@@ -107,15 +106,15 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
### Contributors 🤝
-
-## Contributing
+## 🤝 Contributing
Check out the [Contributing Guide](CONTRIBUTING.md) for more information.
diff --git a/apps/dokploy/__test__/compose/compose.test.ts b/apps/dokploy/__test__/compose/compose.test.ts
index 9d4ba20f5..69d3a5212 100644
--- a/apps/dokploy/__test__/compose/compose.test.ts
+++ b/apps/dokploy/__test__/compose/compose.test.ts
@@ -1,5 +1,5 @@
-import { addSuffixToAllProperties } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToAllProperties } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/config/config-root.test.ts b/apps/dokploy/__test__/compose/config/config-root.test.ts
index 4b40c073e..668e17902 100644
--- a/apps/dokploy/__test__/compose/config/config-root.test.ts
+++ b/apps/dokploy/__test__/compose/config/config-root.test.ts
@@ -1,6 +1,5 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToConfigsRoot } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToConfigsRoot, generateRandomHash } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/config/config-service.test.ts b/apps/dokploy/__test__/compose/config/config-service.test.ts
index de014eb5e..246872f09 100644
--- a/apps/dokploy/__test__/compose/config/config-service.test.ts
+++ b/apps/dokploy/__test__/compose/config/config-service.test.ts
@@ -1,6 +1,8 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToConfigsInServices } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import {
+ addSuffixToConfigsInServices,
+ generateRandomHash,
+} from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/config/config.test.ts b/apps/dokploy/__test__/compose/config/config.test.ts
index aed3350f5..2d5feeb9a 100644
--- a/apps/dokploy/__test__/compose/config/config.test.ts
+++ b/apps/dokploy/__test__/compose/config/config.test.ts
@@ -1,6 +1,5 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToAllConfigs } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToAllConfigs, generateRandomHash } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/domain/labels.test.ts b/apps/dokploy/__test__/compose/domain/labels.test.ts
index 172bff2af..9a75e0a84 100644
--- a/apps/dokploy/__test__/compose/domain/labels.test.ts
+++ b/apps/dokploy/__test__/compose/domain/labels.test.ts
@@ -108,4 +108,136 @@ describe("createDomainLabels", () => {
"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",
+ );
+ });
});
diff --git a/apps/dokploy/__test__/compose/network/network-root.test.ts b/apps/dokploy/__test__/compose/network/network-root.test.ts
index 980502fff..c55f6fa86 100644
--- a/apps/dokploy/__test__/compose/network/network-root.test.ts
+++ b/apps/dokploy/__test__/compose/network/network-root.test.ts
@@ -1,6 +1,5 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToNetworksRoot } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToNetworksRoot, generateRandomHash } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/network/network-service.test.ts b/apps/dokploy/__test__/compose/network/network-service.test.ts
index ee07d9de9..3cf46d4ab 100644
--- a/apps/dokploy/__test__/compose/network/network-service.test.ts
+++ b/apps/dokploy/__test__/compose/network/network-service.test.ts
@@ -1,6 +1,8 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToServiceNetworks } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import {
+ addSuffixToServiceNetworks,
+ generateRandomHash,
+} from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/network/network.test.ts b/apps/dokploy/__test__/compose/network/network.test.ts
index 39cf03958..7ba1c6a83 100644
--- a/apps/dokploy/__test__/compose/network/network.test.ts
+++ b/apps/dokploy/__test__/compose/network/network.test.ts
@@ -1,10 +1,10 @@
-import { generateRandomHash } from "@dokploy/server";
+import type { ComposeSpecification } from "@dokploy/server";
import {
addSuffixToAllNetworks,
+ addSuffixToNetworksRoot,
addSuffixToServiceNetworks,
+ generateRandomHash,
} from "@dokploy/server";
-import { addSuffixToNetworksRoot } from "@dokploy/server";
-import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/secrets/secret-root.test.ts b/apps/dokploy/__test__/compose/secrets/secret-root.test.ts
index 1b1898c59..b8cef56e4 100644
--- a/apps/dokploy/__test__/compose/secrets/secret-root.test.ts
+++ b/apps/dokploy/__test__/compose/secrets/secret-root.test.ts
@@ -1,6 +1,5 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToSecretsRoot } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToSecretsRoot, generateRandomHash } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/secrets/secret-services.test.ts b/apps/dokploy/__test__/compose/secrets/secret-services.test.ts
index 5206bbbaf..e12f611d0 100644
--- a/apps/dokploy/__test__/compose/secrets/secret-services.test.ts
+++ b/apps/dokploy/__test__/compose/secrets/secret-services.test.ts
@@ -1,6 +1,8 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToSecretsInServices } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import {
+ addSuffixToSecretsInServices,
+ generateRandomHash,
+} from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/secrets/secret.test.ts b/apps/dokploy/__test__/compose/secrets/secret.test.ts
index d874dc5e7..3ff524ad7 100644
--- a/apps/dokploy/__test__/compose/secrets/secret.test.ts
+++ b/apps/dokploy/__test__/compose/secrets/secret.test.ts
@@ -1,5 +1,5 @@
-import { addSuffixToAllSecrets } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToAllSecrets } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/service/service-container-name.test.ts b/apps/dokploy/__test__/compose/service/service-container-name.test.ts
index bcb51fd04..6ad45c588 100644
--- a/apps/dokploy/__test__/compose/service/service-container-name.test.ts
+++ b/apps/dokploy/__test__/compose/service/service-container-name.test.ts
@@ -1,6 +1,5 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToServiceNames } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/service/service-depends-on.test.ts b/apps/dokploy/__test__/compose/service/service-depends-on.test.ts
index b27414be5..14a5789c4 100644
--- a/apps/dokploy/__test__/compose/service/service-depends-on.test.ts
+++ b/apps/dokploy/__test__/compose/service/service-depends-on.test.ts
@@ -1,6 +1,5 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToServiceNames } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/service/service-extends.test.ts b/apps/dokploy/__test__/compose/service/service-extends.test.ts
index 8309a32fd..0b7e92c53 100644
--- a/apps/dokploy/__test__/compose/service/service-extends.test.ts
+++ b/apps/dokploy/__test__/compose/service/service-extends.test.ts
@@ -1,6 +1,5 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToServiceNames } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/service/service-links.test.ts b/apps/dokploy/__test__/compose/service/service-links.test.ts
index 5f9b01ab2..6c8cde39e 100644
--- a/apps/dokploy/__test__/compose/service/service-links.test.ts
+++ b/apps/dokploy/__test__/compose/service/service-links.test.ts
@@ -1,6 +1,5 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToServiceNames } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/service/service-names.test.ts b/apps/dokploy/__test__/compose/service/service-names.test.ts
index 936a32ecc..c65299b03 100644
--- a/apps/dokploy/__test__/compose/service/service-names.test.ts
+++ b/apps/dokploy/__test__/compose/service/service-names.test.ts
@@ -1,6 +1,5 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToServiceNames } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/service/service.test.ts b/apps/dokploy/__test__/compose/service/service.test.ts
index c6050f75a..38895e073 100644
--- a/apps/dokploy/__test__/compose/service/service.test.ts
+++ b/apps/dokploy/__test__/compose/service/service.test.ts
@@ -1,8 +1,8 @@
+import type { ComposeSpecification } from "@dokploy/server";
import {
addSuffixToAllServiceNames,
addSuffixToServiceNames,
} from "@dokploy/server";
-import type { ComposeSpecification } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts b/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts
index 8066a6dd7..8aa8296e8 100644
--- a/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts
+++ b/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts
@@ -1,6 +1,5 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToServiceNames } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToServiceNames, generateRandomHash } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/volume/volume-2.test.ts b/apps/dokploy/__test__/compose/volume/volume-2.test.ts
index 61cba82d3..6aa9d01d3 100644
--- a/apps/dokploy/__test__/compose/volume/volume-2.test.ts
+++ b/apps/dokploy/__test__/compose/volume/volume-2.test.ts
@@ -1,6 +1,9 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToAllVolumes, addSuffixToVolumesRoot } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import {
+ addSuffixToAllVolumes,
+ addSuffixToVolumesRoot,
+ generateRandomHash,
+} from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/volume/volume-root.test.ts b/apps/dokploy/__test__/compose/volume/volume-root.test.ts
index d91cb64d3..80db1f0cc 100644
--- a/apps/dokploy/__test__/compose/volume/volume-root.test.ts
+++ b/apps/dokploy/__test__/compose/volume/volume-root.test.ts
@@ -1,6 +1,5 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToVolumesRoot } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToVolumesRoot, generateRandomHash } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/volume/volume-services.test.ts b/apps/dokploy/__test__/compose/volume/volume-services.test.ts
index 04a1a45ae..0e9cb018f 100644
--- a/apps/dokploy/__test__/compose/volume/volume-services.test.ts
+++ b/apps/dokploy/__test__/compose/volume/volume-services.test.ts
@@ -1,6 +1,8 @@
-import { generateRandomHash } from "@dokploy/server";
-import { addSuffixToVolumesInServices } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import {
+ addSuffixToVolumesInServices,
+ generateRandomHash,
+} from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/compose/volume/volume.test.ts b/apps/dokploy/__test__/compose/volume/volume.test.ts
index 6c4344762..6f8e76708 100644
--- a/apps/dokploy/__test__/compose/volume/volume.test.ts
+++ b/apps/dokploy/__test__/compose/volume/volume.test.ts
@@ -1,5 +1,5 @@
-import { addSuffixToAllVolumes } from "@dokploy/server";
import type { ComposeSpecification } from "@dokploy/server";
+import { addSuffixToAllVolumes } from "@dokploy/server";
import { load } from "js-yaml";
import { expect, test } from "vitest";
diff --git a/apps/dokploy/__test__/deploy/github.test.ts b/apps/dokploy/__test__/deploy/github.test.ts
index 18d7619ab..03805b08d 100644
--- a/apps/dokploy/__test__/deploy/github.test.ts
+++ b/apps/dokploy/__test__/deploy/github.test.ts
@@ -1,5 +1,5 @@
-import { extractCommitMessage } from "@/pages/api/deploy/[refreshToken]";
import { describe, expect, it } from "vitest";
+import { extractCommitMessage } from "@/pages/api/deploy/[refreshToken]";
describe("GitHub Webhook Skip CI", () => {
const mockGithubHeaders = {
diff --git a/apps/dokploy/__test__/drop/drop.test.test.ts b/apps/dokploy/__test__/drop/drop.test.ts
similarity index 99%
rename from apps/dokploy/__test__/drop/drop.test.test.ts
rename to apps/dokploy/__test__/drop/drop.test.ts
index 37a337adc..496949481 100644
--- a/apps/dokploy/__test__/drop/drop.test.test.ts
+++ b/apps/dokploy/__test__/drop/drop.test.ts
@@ -1,12 +1,12 @@
import fs from "node:fs/promises";
import path from "node:path";
-import { paths } from "@dokploy/server/constants";
-const { APPLICATIONS_PATH } = paths();
import type { ApplicationNested } from "@dokploy/server";
import { unzipDrop } from "@dokploy/server";
+import { paths } from "@dokploy/server/constants";
import AdmZip from "adm-zip";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
+const { APPLICATIONS_PATH } = paths();
vi.mock("@dokploy/server/constants", async (importOriginal) => {
const actual = await importOriginal();
return {
@@ -143,7 +143,7 @@ describe("unzipDrop using real zip files", () => {
const outputPath = path.join(APPLICATIONS_PATH, baseApp.appName, "code");
const zip = new AdmZip("./__test__/drop/zips/single-file.zip");
console.log(`Output Path: ${outputPath}`);
- const zipBuffer = zip.toBuffer();
+ const zipBuffer = zip.toBuffer() as Buffer+ Fetch: Will clone the repository and + load the services +
++ Cache: If you previously deployed this + compose, it will read the services + from the last deployment/fetch from + the repository +
+- Fetch: Will clone the repository and load - the services -
-- Cache: If you previously deployed this - compose, it will read the services from - the last deployment/fetch from the - repository + {isManualInput + ? "Switch to service selection" + : "Enter service name manually"}
+ This will be the name of the Docker Swarm service +
++ This will be the name of the Docker Swarm + service +
+Generating template suggestions based on your input...
-{templateInfo.userInput}
+ {templateInfo.userInput}