diff --git a/.github/sponsors/hostinger.jpg b/.github/sponsors/hostinger.jpg
new file mode 100644
index 00000000..f9b8230b
Binary files /dev/null and b/.github/sponsors/hostinger.jpg differ
diff --git a/README.md b/README.md
index 668f40bb..0df55f20 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
Join us on Discord for help, feedback, and discussions!
-
+
@@ -56,6 +56,10 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
+### Hero Sponsors 🎖
+
+
+
### Premium Supporters 🥇
diff --git a/apps/docs/content/docs/core/get-started/installation.mdx b/apps/docs/content/docs/core/get-started/installation.mdx
index 2d424c67..b0fef8fc 100644
--- a/apps/docs/content/docs/core/get-started/installation.mdx
+++ b/apps/docs/content/docs/core/get-started/installation.mdx
@@ -69,4 +69,4 @@ Open your web browser and navigate to `http://your-ip-from-your-vps:3000`. You w
1. **Create an Admin Account:** Fill in the necessary details to set up your administrator account. This account will be the admin account for Dokploy.
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/core/get-started/introduction.mdx b/apps/docs/content/docs/core/get-started/introduction.mdx
index 2c35b4ac..551e8040 100644
--- a/apps/docs/content/docs/core/get-started/introduction.mdx
+++ b/apps/docs/content/docs/core/get-started/introduction.mdx
@@ -32,22 +32,22 @@ Please go to get started.
diff --git a/apps/docs/public/assets/images/setup.png b/apps/docs/public/assets/images/setup.png
index 1e68801d..ff0cae02 100644
Binary files a/apps/docs/public/assets/images/setup.png and b/apps/docs/public/assets/images/setup.png differ
diff --git a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx
index 46c05f15..90112e3c 100644
--- a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx
+++ b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx
@@ -71,7 +71,10 @@ export const ShowDomains = ({ applicationId }: Props) => {
key={item.domainId}
className="flex w-full items-center gap-4 max-sm:flex-wrap border p-4 rounded-lg"
>
-
+
diff --git a/apps/dokploy/components/dashboard/settings/web-server/edit-traefik-env.tsx b/apps/dokploy/components/dashboard/settings/web-server/edit-traefik-env.tsx
index c0504ab4..fb9a3aeb 100644
--- a/apps/dokploy/components/dashboard/settings/web-server/edit-traefik-env.tsx
+++ b/apps/dokploy/components/dashboard/settings/web-server/edit-traefik-env.tsx
@@ -132,7 +132,7 @@ TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_HTTP_CHALLENGE_DNS_PROVIDER=cloudflare
diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json
index 6e3e75f7..2ec8d99e 100644
--- a/apps/dokploy/package.json
+++ b/apps/dokploy/package.json
@@ -1,6 +1,6 @@
{
"name": "dokploy",
- "version": "v0.7.1",
+ "version": "v0.7.3",
"private": true,
"license": "Apache-2.0",
"type": "module",
diff --git a/apps/dokploy/public/templates/typebot.svg b/apps/dokploy/public/templates/typebot.svg
new file mode 100644
index 00000000..83d04de2
--- /dev/null
+++ b/apps/dokploy/public/templates/typebot.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/dokploy/public/templates/zipline.png b/apps/dokploy/public/templates/zipline.png
new file mode 100644
index 00000000..2b8f6972
Binary files /dev/null and b/apps/dokploy/public/templates/zipline.png differ
diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts
index 262c9ac2..f3b2362f 100644
--- a/apps/dokploy/server/api/routers/auth.ts
+++ b/apps/dokploy/server/api/routers/auth.ts
@@ -12,6 +12,7 @@ import {
} from "@/server/db/schema";
import { TRPCError } from "@trpc/server";
import * as bcrypt from "bcrypt";
+import { db } from "../../db";
import {
createAdmin,
createUser,
@@ -33,6 +34,14 @@ export const authRouter = createTRPCRouter({
.input(apiCreateAdmin)
.mutation(async ({ ctx, input }) => {
try {
+ const admin = await db.query.admins.findFirst({});
+
+ if (admin) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Admin already exists",
+ });
+ }
const newAdmin = await createAdmin(input);
const session = await lucia.createSession(newAdmin.id || "", {});
ctx.res.appendHeader(
diff --git a/apps/dokploy/server/setup/traefik-setup.ts b/apps/dokploy/server/setup/traefik-setup.ts
index afebf07e..15542a00 100644
--- a/apps/dokploy/server/setup/traefik-setup.ts
+++ b/apps/dokploy/server/setup/traefik-setup.ts
@@ -1,6 +1,6 @@
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
import path from "node:path";
-import type { CreateServiceOptions } from "dockerode";
+import type { ContainerTaskSpec, CreateServiceOptions } from "dockerode";
import { dump } from "js-yaml";
import { DYNAMIC_TRAEFIK_PATH, MAIN_TRAEFIK_PATH, docker } from "../constants";
import { pullImage } from "../utils/docker/utils";
@@ -18,7 +18,7 @@ interface TraefikOptions {
export const initializeTraefik = async ({
enableDashboard = false,
- env = [],
+ env,
}: TraefikOptions = {}) => {
const imageName = "traefik:v2.5";
const containerName = "dokploy-traefik";
@@ -85,9 +85,23 @@ export const initializeTraefik = async ({
const service = docker.getService(containerName);
const inspect = await service.inspect();
+
+ const existingEnv = inspect.Spec.TaskTemplate.ContainerSpec.Env || [];
+ const updatedEnv = !env ? existingEnv : env;
+
+ const updatedSettings = {
+ ...settings,
+ TaskTemplate: {
+ ...settings.TaskTemplate,
+ ContainerSpec: {
+ ...(settings?.TaskTemplate as ContainerTaskSpec).ContainerSpec,
+ Env: updatedEnv,
+ },
+ },
+ };
await service.update({
version: Number.parseInt(inspect.Version.Index),
- ...settings,
+ ...updatedSettings,
});
console.log("Traefik Started ✅");
diff --git a/apps/dokploy/server/utils/docker/utils.ts b/apps/dokploy/server/utils/docker/utils.ts
index e981aec0..68411b4c 100644
--- a/apps/dokploy/server/utils/docker/utils.ts
+++ b/apps/dokploy/server/utils/docker/utils.ts
@@ -109,7 +109,7 @@ export const cleanStoppedContainers = async () => {
export const cleanUpUnusedVolumes = async () => {
try {
- await execAsync("docker volume prune --force");
+ await execAsync("docker volume prune --all --force");
} catch (error) {
console.error(error);
throw error;
diff --git a/apps/dokploy/templates/documenso/docker-compose.yml b/apps/dokploy/templates/documenso/docker-compose.yml
index a649fa75..562fe498 100644
--- a/apps/dokploy/templates/documenso/docker-compose.yml
+++ b/apps/dokploy/templates/documenso/docker-compose.yml
@@ -18,7 +18,7 @@ services:
start_period: 10s
documenso:
- image: documenso/documenso:1.5.6-rc.2
+ image: documenso/documenso:v1.5.6-rc.2
depends_on:
postgres:
condition: service_healthy
diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts
index 100802e5..eca30ee5 100644
--- a/apps/dokploy/templates/templates.ts
+++ b/apps/dokploy/templates/templates.ts
@@ -423,6 +423,21 @@ export const templates: TemplateData[] = [
tags: ["database", "spreadsheet", "low-code", "nocode"],
load: () => import("./teable/index").then((m) => m.generate),
},
+ {
+ id: "zipline",
+ name: "Zipline",
+ version: "v3.7.9",
+ description:
+ "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!",
+ logo: "zipline.png",
+ links: {
+ github: "https://github.com/diced/zipline",
+ website: "https://zipline.diced.sh/",
+ docs: "https://zipline.diced.sh/docs/",
+ },
+ tags: ["media system", "storage"],
+ load: () => import("./zipline/index").then((m) => m.generate),
+ },
{
id: "soketi",
name: "Soketi",
@@ -453,4 +468,18 @@ export const templates: TemplateData[] = [
tags: ["analytics", "self-hosted"],
load: () => import("./aptabase/index").then((m) => m.generate),
},
+ {
+ id: "typebot",
+ name: "Typebot",
+ version: "2.27.0",
+ description: "Typebot is an open-source chatbot builder platform.",
+ logo: "typebot.svg",
+ links: {
+ github: "https://github.com/baptisteArno/typebot.io",
+ website: "https://typebot.io/",
+ docs: "https://docs.typebot.io/get-started/introduction",
+ },
+ tags: ["chatbot", "builder", "open-source"],
+ load: () => import("./typebot/index").then((m) => m.generate),
+ },
];
diff --git a/apps/dokploy/templates/typebot/docker-compose.yml b/apps/dokploy/templates/typebot/docker-compose.yml
new file mode 100644
index 00000000..739793fe
--- /dev/null
+++ b/apps/dokploy/templates/typebot/docker-compose.yml
@@ -0,0 +1,49 @@
+version: '3.3'
+
+volumes:
+ db-data:
+
+services:
+ typebot-db:
+ image: postgres:14-alpine
+ restart: always
+ volumes:
+ - db-data:/var/lib/postgresql/data
+ environment:
+ POSTGRES_USER: typebot
+ POSTGRES_DB: typebot
+ POSTGRES_PASSWORD: typebot
+ networks:
+ - dokploy-network
+
+ typebot-builder:
+ image: baptistearno/typebot-builder:2.27
+ restart: always
+ depends_on:
+ - typebot-db
+ environment:
+ ENCRYPTION_SECRET: '${ENCRYPTION_SECRET}'
+ DATABASE_URL: 'postgresql://typebot:typebot@typebot-db:5432/typebot'
+ NEXTAUTH_URL: '${NEXTAUTH_URL}'
+ NEXT_PUBLIC_VIEWER_URL: '${NEXT_PUBLIC_VIEWER_URL}'
+ ADMIN_EMAIL: '${ADMIN_EMAIL}'
+ SMTP_HOST: '${SMTP_HOST}'
+ NEXT_PUBLIC_SMTP_FROM: '${NEXT_PUBLIC_SMTP_FROM}'
+ SMTP_USERNAME: '${SMTP_USERNAME}'
+ SMTP_PASSWORD: '${SMTP_PASSWORD}'
+ DEFAULT_WORKSPACE_PLAN: '${DEFAULT_WORKSPACE_PLAN}'
+
+ typebot-viewer:
+ image: baptistearno/typebot-viewer:2.27.0
+ restart: always
+ environment:
+ ENCRYPTION_SECRET: '${ENCRYPTION_SECRET}'
+ DATABASE_URL: postgresql://typebot:typebot@typebot-db:5432/typebot
+ NEXTAUTH_URL: '${NEXTAUTH_URL}'
+ NEXT_PUBLIC_VIEWER_URL: '${NEXT_PUBLIC_VIEWER_URL}'
+ ADMIN_EMAIL: '${ADMIN_EMAIL}'
+ SMTP_HOST: '${SMTP_HOST}'
+ NEXT_PUBLIC_SMTP_FROM: '${NEXT_PUBLIC_SMTP_FROM}'
+ SMTP_USERNAME: '${SMTP_USERNAME}'
+ SMTP_PASSWORD: '${SMTP_PASSWORD}'
+ DEFAULT_WORKSPACE_PLAN: '${DEFAULT_WORKSPACE_PLAN}'
\ No newline at end of file
diff --git a/apps/dokploy/templates/typebot/index.ts b/apps/dokploy/templates/typebot/index.ts
new file mode 100644
index 00000000..87a7fe83
--- /dev/null
+++ b/apps/dokploy/templates/typebot/index.ts
@@ -0,0 +1,44 @@
+import {
+ type DomainSchema,
+ type Schema,
+ type Template,
+ generateBase64,
+ generateRandomDomain,
+} from "../utils";
+
+export function generate(schema: Schema): Template {
+ const builderDomain = generateRandomDomain(schema);
+ const viewerDomain = generateRandomDomain(schema);
+ const encryptionSecret = generateBase64(24);
+
+ const domains: DomainSchema[] = [
+ {
+ host: builderDomain,
+ port: 3000,
+ serviceName: "typebot-builder",
+ },
+ {
+ host: viewerDomain,
+ port: 3000,
+ serviceName: "typebot-viewer",
+ },
+ ];
+
+ const envs = [
+ `ENCRYPTION_SECRET=${encryptionSecret}`,
+ `NEXTAUTH_URL=http://${builderDomain}`,
+ `NEXT_PUBLIC_VIEWER_URL=http://${viewerDomain}`,
+ "ADMIN_EMAIL=typebot@example.com",
+ "SMTP_HOST='Fill'",
+ "SMTP_PORT=25",
+ "SMTP_USERNAME='Fill'",
+ "SMTP_PASSWORD='Fill'",
+ "NEXT_PUBLIC_SMTP_FROM=typebot@example.com",
+ "DEFAULT_WORKSPACE_PLAN=UNLIMITED",
+ ];
+
+ return {
+ envs,
+ domains,
+ };
+}
diff --git a/apps/dokploy/templates/umami/docker-compose.yml b/apps/dokploy/templates/umami/docker-compose.yml
index a149a5e6..22876210 100644
--- a/apps/dokploy/templates/umami/docker-compose.yml
+++ b/apps/dokploy/templates/umami/docker-compose.yml
@@ -1,6 +1,6 @@
services:
umami:
- image: ghcr.io/umami-software/umami:postgresql-v2.12.1
+ image: ghcr.io/umami-software/umami:postgresql-v2.13.2
restart: always
healthcheck:
test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
diff --git a/apps/dokploy/templates/zipline/docker-compose.yml b/apps/dokploy/templates/zipline/docker-compose.yml
new file mode 100644
index 00000000..808b0b89
--- /dev/null
+++ b/apps/dokploy/templates/zipline/docker-compose.yml
@@ -0,0 +1,37 @@
+version: "3"
+services:
+ postgres:
+ image: postgres:15
+ networks:
+ - dokploy-network
+ restart: unless-stopped
+ environment:
+ - POSTGRES_USER=postgres
+ - POSTGRES_PASSWORD=postgres
+ - POSTGRES_DATABASE=postgres
+ volumes:
+ - pg_data:/var/lib/postgresql/data
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ zipline:
+ image: ghcr.io/diced/zipline:3.7.9
+ restart: unless-stopped
+ environment:
+ - CORE_RETURN_HTTPS=${ZIPLINE_RETURN_HTTPS}
+ - CORE_SECRET=${ZIPLINE_SECRET}
+ - CORE_HOST=0.0.0.0
+ - CORE_PORT=${ZIPLINE_PORT}
+ - CORE_DATABASE_URL=postgres://postgres:postgres@postgres/postgres
+ - CORE_LOGGER=${ZIPLINE_LOGGER}
+ volumes:
+ - "../files/uploads:/zipline/uploads"
+ - "../files/public:/zipline/public"
+ depends_on:
+ - "postgres"
+
+volumes:
+ pg_data:
diff --git a/apps/dokploy/templates/zipline/index.ts b/apps/dokploy/templates/zipline/index.ts
new file mode 100644
index 00000000..ce0172e4
--- /dev/null
+++ b/apps/dokploy/templates/zipline/index.ts
@@ -0,0 +1,32 @@
+import {
+ type DomainSchema,
+ type Schema,
+ type Template,
+ generateBase64,
+ generateRandomDomain,
+} from "@/templates/utils";
+
+export function generate(schema: Schema): Template {
+ const randomDomain = generateRandomDomain(schema);
+ const secretBase = generateBase64(64);
+
+ const domains: DomainSchema[] = [
+ {
+ host: randomDomain,
+ port: 3000,
+ serviceName: "zipline",
+ },
+ ];
+
+ const envs = [
+ "ZIPLINE_PORT=3000",
+ `ZIPLINE_SECRET=${secretBase}`,
+ "ZIPLINE_RETURN_HTTPS=false",
+ "ZIPLINE_LOGGER=true",
+ ];
+
+ return {
+ envs,
+ domains,
+ };
+}