diff --git a/components/dashboard/application/domains/generate-domain.tsx b/components/dashboard/application/domains/generate-domain.tsx
new file mode 100644
index 000000000..92ca19bd2
--- /dev/null
+++ b/components/dashboard/application/domains/generate-domain.tsx
@@ -0,0 +1,79 @@
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { RefreshCcw } from "lucide-react";
+import { GenerateTraefikMe } from "./generate-traefikme";
+import { GenerateWildCard } from "./generate-wildcard";
+import Link from "next/link";
+import { api } from "@/utils/api";
+
+interface Props {
+ applicationId: string;
+}
+
+export const GenerateDomain = ({ applicationId }: Props) => {
+ return (
+
+
+
+ Generate Domain
+
+
+
+
+
+ Generate Domain
+
+ Generate Domains for your applications
+
+
+
+
+
+
+
+
+ 1. Generate TraefikMe Domain
+
+
+ This option generates a free domain provided by{" "}
+
+ TraefikMe
+
+ . We recommend using this for quick domain testing or if you
+ don't have a domain yet.
+
+
+
+ {/*
+
+
+ 2. Use Wildcard Domain
+
+
+ To use this option, you need to set up an 'A' record in your
+ domain provider. For example, create a record for
+ *.yourdomain.com.
+
+
+ */}
+
+
+
+ {/* */}
+
+
+
+
+ );
+};
diff --git a/components/dashboard/application/domains/generate-traefikme.tsx b/components/dashboard/application/domains/generate-traefikme.tsx
new file mode 100644
index 000000000..264a626f1
--- /dev/null
+++ b/components/dashboard/application/domains/generate-traefikme.tsx
@@ -0,0 +1,69 @@
+import React from "react";
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from "@/components/ui/alert-dialog";
+import { Button } from "@/components/ui/button";
+import { api } from "@/utils/api";
+import { RefreshCcw } from "lucide-react";
+import { toast } from "sonner";
+
+interface Props {
+ applicationId: string;
+}
+export const GenerateTraefikMe = ({ applicationId }: Props) => {
+ const { mutateAsync, isLoading } = api.domain.generateDomain.useMutation();
+ const utils = api.useUtils();
+ return (
+
+
+
+ Generate Domain
+
+
+
+
+
+
+ Are you sure to generate a new domain?
+
+
+ This will generate a new domain and will be used to access to the
+ application
+
+
+
+ Cancel
+ {
+ await mutateAsync({
+ applicationId,
+ })
+ .then((data) => {
+ utils.domain.byApplicationId.invalidate({
+ applicationId: applicationId,
+ });
+ utils.application.readTraefikConfig.invalidate({
+ applicationId: applicationId,
+ });
+ toast.success("Generated Domain succesfully");
+ })
+ .catch(() => {
+ toast.error("Error to generate Domain");
+ });
+ }}
+ >
+ Confirm
+
+
+
+
+ );
+};
diff --git a/components/dashboard/application/domains/generate-wildcard.tsx b/components/dashboard/application/domains/generate-wildcard.tsx
new file mode 100644
index 000000000..11babebdb
--- /dev/null
+++ b/components/dashboard/application/domains/generate-wildcard.tsx
@@ -0,0 +1,69 @@
+import React from "react";
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from "@/components/ui/alert-dialog";
+import { Button } from "@/components/ui/button";
+import { api } from "@/utils/api";
+import { SquareAsterisk } from "lucide-react";
+import { toast } from "sonner";
+
+interface Props {
+ applicationId: string;
+}
+export const GenerateWildCard = ({ applicationId }: Props) => {
+ const { mutateAsync, isLoading } = api.domain.generateWildcard.useMutation();
+ const utils = api.useUtils();
+ return (
+
+
+
+ Generate Wildcard Domain
+
+
+
+
+
+
+ Are you sure to generate a new wildcard domain?
+
+
+ This will generate a new domain and will be used to access to the
+ application
+
+
+
+ Cancel
+ {
+ await mutateAsync({
+ applicationId,
+ })
+ .then((data) => {
+ utils.domain.byApplicationId.invalidate({
+ applicationId: applicationId,
+ });
+ utils.application.readTraefikConfig.invalidate({
+ applicationId: applicationId,
+ });
+ toast.success("Generated Domain succesfully");
+ })
+ .catch((e) => {
+ toast.error(`Error to generate Domain: ${e.message}`);
+ });
+ }}
+ >
+ Confirm
+
+
+
+
+ );
+};
diff --git a/components/dashboard/application/domains/show-domains.tsx b/components/dashboard/application/domains/show-domains.tsx
index c12d91401..d4df0366a 100644
--- a/components/dashboard/application/domains/show-domains.tsx
+++ b/components/dashboard/application/domains/show-domains.tsx
@@ -6,7 +6,7 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
-import { ExternalLink, GlobeIcon } from "lucide-react";
+import { ExternalLink, GlobeIcon, RefreshCcw } from "lucide-react";
import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
import { Input } from "@/components/ui/input";
@@ -14,6 +14,7 @@ import { DeleteDomain } from "./delete-domain";
import Link from "next/link";
import { AddDomain } from "./add-domain";
import { UpdateDomain } from "./update-domain";
+import { GenerateDomain } from "./generate-domain";
interface Props {
applicationId: string;
@@ -31,7 +32,7 @@ export const ShowDomains = ({ applicationId }: Props) => {
return (
-
+
Domains
@@ -39,11 +40,16 @@ export const ShowDomains = ({ applicationId }: Props) => {
- {data && data?.length > 0 && (
-
- Add Domain
-
- )}
+
+ {data && data?.length > 0 && (
+
+ Add Domain
+
+ )}
+ {data && data?.length > 0 && (
+
+ )}
+
{data?.length === 0 ? (
@@ -53,9 +59,13 @@ export const ShowDomains = ({ applicationId }: Props) => {
To access to the application is required to set at least 1
domain
-
- Add Domain
-
+
) : (
diff --git a/components/dashboard/monitoring/docker/show.tsx b/components/dashboard/monitoring/docker/show.tsx
index 0fae337ac..ea7556b58 100644
--- a/components/dashboard/monitoring/docker/show.tsx
+++ b/components/dashboard/monitoring/docker/show.tsx
@@ -141,7 +141,13 @@ export const DockerMonitoring = ({
network: data.network[data.network.length - 1] ?? currentData.network,
disk: data.disk[data.disk.length - 1] ?? currentData.disk,
});
- setAcummulativeData(data);
+ setAcummulativeData({
+ block: data?.block || [],
+ cpu: data?.cpu || [],
+ disk: data?.disk || [],
+ memory: data?.memory || [],
+ network: data?.network || [],
+ });
}, [data]);
useEffect(() => {
diff --git a/components/dashboard/projects/show.tsx b/components/dashboard/projects/show.tsx
index 124a447d1..8df235833 100644
--- a/components/dashboard/projects/show.tsx
+++ b/components/dashboard/projects/show.tsx
@@ -76,8 +76,8 @@ export const ShowProjects = () => {
project?.compose.length;
return (
-
-
+
+
{
>
-
-
-
-
-
-
-
- {project.name}
-
-
+
+
+
+
+
+
+ {project.name}
+
+
-
- {project.description}
+
+ {project.description}
+
-
-
-
-
-
-
-
-
-
-
- Actions
-
+
+
+
+
+
+
+
+
+
+ Actions
+
-
+ e.stopPropagation()}>
+
+
- {(auth?.rol === "admin" ||
- user?.canDeleteProjects) && (
-
-
- e.preventDefault()}
- >
-
- Delete
-
-
-
-
-
- Are you sure to delete this project?
-
- {!emptyServices ? (
-
-
-
- You have active services, please delete
- them first
-
-
- ) : (
-
- This action cannot be undone
-
- )}
-
-
- Cancel
- {
- await mutateAsync({
- projectId: project.projectId,
- })
- .then(() => {
- toast.success(
- "Project delete succesfully",
- );
- })
- .catch(() => {
- toast.error(
- "Error to delete this project",
- );
- })
- .finally(() => {
- utils.project.all.invalidate();
- });
- }}
- >
- Delete
-
-
-
-
- )}
-
-
+
e.stopPropagation()}>
+ {(auth?.rol === "admin" ||
+ user?.canDeleteProjects) && (
+
+
+ e.preventDefault()}
+ >
+
+ Delete
+
+
+
+
+
+ Are you sure to delete this project?
+
+ {!emptyServices ? (
+
+
+
+ You have active services, please
+ delete them first
+
+
+ ) : (
+
+ This action cannot be undone
+
+ )}
+
+
+
+ Cancel
+
+ {
+ await mutateAsync({
+ projectId: project.projectId,
+ })
+ .then(() => {
+ toast.success(
+ "Project delete succesfully",
+ );
+ })
+ .catch(() => {
+ toast.error(
+ "Error to delete this project",
+ );
+ })
+ .finally(() => {
+ utils.project.all.invalidate();
+ });
+ }}
+ >
+ Delete
+
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+ Created
+
+
+ {totalServices}{" "}
+ {totalServices === 1 ? "service" : "services"}
+
-
-
-
-
- Created
-
- {totalServices}{" "}
- {totalServices === 1 ? "service" : "services"}
-
-
-
-
+
+
+
);
})}
diff --git a/drizzle/0017_minor_post.sql b/drizzle/0017_minor_post.sql
new file mode 100644
index 000000000..80a71d095
--- /dev/null
+++ b/drizzle/0017_minor_post.sql
@@ -0,0 +1 @@
+ALTER TABLE "deployment" ADD COLUMN "description" text;
\ No newline at end of file
diff --git a/drizzle/0017_yummy_norrin_radd.sql b/drizzle/0017_yummy_norrin_radd.sql
deleted file mode 100644
index 570a343e1..000000000
--- a/drizzle/0017_yummy_norrin_radd.sql
+++ /dev/null
@@ -1 +0,0 @@
-ALTER TABLE "user" ALTER COLUMN "token" DROP NOT NULL;
\ No newline at end of file
diff --git a/drizzle/meta/0017_snapshot.json b/drizzle/meta/0017_snapshot.json
index 83f744c5f..afeb4aa35 100644
--- a/drizzle/meta/0017_snapshot.json
+++ b/drizzle/meta/0017_snapshot.json
@@ -1,5 +1,5 @@
{
- "id": "7610c85e-c3e4-4a32-8ce9-7f48b298f956",
+ "id": "ec852f38-886a-43b4-9295-73984ed8ef45",
"prevId": "2d8d7670-b942-4573-9c44-6e81d2a2fa16",
"version": "6",
"dialect": "postgresql",
@@ -465,7 +465,7 @@
"name": "token",
"type": "text",
"primaryKey": false,
- "notNull": false
+ "notNull": true
},
"isRegistered": {
"name": "isRegistered",
@@ -1585,6 +1585,12 @@
"primaryKey": false,
"notNull": true
},
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
"status": {
"name": "status",
"type": "deploymentStatus",
diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json
index 79bef40a6..7a8a186f6 100644
--- a/drizzle/meta/_journal.json
+++ b/drizzle/meta/_journal.json
@@ -124,8 +124,8 @@
{
"idx": 17,
"version": "6",
- "when": 1719109531147,
- "tag": "0017_yummy_norrin_radd",
+ "when": 1719547174326,
+ "tag": "0017_minor_post",
"breakpoints": true
}
]
diff --git a/package.json b/package.json
index 8442d502b..6745af92b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "dokploy",
- "version": "v0.2.4",
+ "version": "v0.2.5",
"private": true,
"license": "AGPL-3.0-only",
"type": "module",
diff --git a/pages/api/[...trpc].ts b/pages/api/[...trpc].ts
new file mode 100644
index 000000000..7f1d41d16
--- /dev/null
+++ b/pages/api/[...trpc].ts
@@ -0,0 +1,29 @@
+import type { NextApiRequest, NextApiResponse } from "next";
+import { createOpenApiNextHandler } from "@dokploy/trpc-openapi";
+import { appRouter } from "@/server/api/root";
+import { createTRPCContext } from "@/server/api/trpc";
+import { validateBearerToken } from "@/server/auth/token";
+import { validateRequest } from "@/server/auth/auth";
+
+const handler = async (req: NextApiRequest, res: NextApiResponse) => {
+ let { session, user } = await validateBearerToken(req);
+
+ if (!session) {
+ const cookieResult = await validateRequest(req, res);
+ session = cookieResult.session;
+ user = cookieResult.user;
+ }
+
+ if (!user || !session) {
+ res.status(401).json({ message: "Unauthorized" });
+ return;
+ }
+
+ // @ts-ignore
+ return createOpenApiNextHandler({
+ router: appRouter,
+ createContext: createTRPCContext,
+ })(req, res);
+};
+
+export default handler;
diff --git a/pages/api/deploy/[refreshToken].ts b/pages/api/deploy/[refreshToken].ts
index 1e6326641..c2aa3f16f 100644
--- a/pages/api/deploy/[refreshToken].ts
+++ b/pages/api/deploy/[refreshToken].ts
@@ -33,6 +33,7 @@ export default async function handler(
}
const deploymentTitle = extractCommitMessage(req.headers, req.body);
+ const deploymentHash = extractHash(req.headers, req.body);
const sourceType = application.sourceType;
@@ -75,6 +76,7 @@ export default async function handler(
const jobData: DeploymentJob = {
applicationId: application.applicationId as string,
titleLog: deploymentTitle,
+ descriptionLog: `Hash: ${deploymentHash}`,
type: "deploy",
applicationType: "application",
};
@@ -166,6 +168,37 @@ export const extractCommitMessage = (headers: any, body: any) => {
return "NEW CHANGES";
};
+export const extractHash = (headers: any, body: any) => {
+ // GitHub
+ if (headers["x-github-event"]) {
+ return body.head_commit ? body.head_commit.id : "";
+ }
+
+ // GitLab
+ if (headers["x-gitlab-event"]) {
+ return (
+ body.checkout_sha ||
+ (body.commits && body.commits.length > 0
+ ? body.commits[0].id
+ : "NEW COMMIT")
+ );
+ }
+
+ // Bitbucket
+ if (headers["x-event-key"]?.includes("repo:push")) {
+ return body.push.changes && body.push.changes.length > 0
+ ? body.push.changes[0].new.target.hash
+ : "NEW COMMIT";
+ }
+
+ // Gitea
+ if (headers["x-gitea-event"]) {
+ return body.after || "NEW COMMIT";
+ }
+
+ return "";
+};
+
export const extractBranchName = (headers: any, body: any) => {
if (headers["x-github-event"] || headers["x-gitea-event"]) {
return body?.ref?.replace("refs/heads/", "");
diff --git a/pages/api/deploy/compose/[refreshToken].ts b/pages/api/deploy/compose/[refreshToken].ts
index 4f9b121b2..1c5739818 100644
--- a/pages/api/deploy/compose/[refreshToken].ts
+++ b/pages/api/deploy/compose/[refreshToken].ts
@@ -4,7 +4,11 @@ import type { DeploymentJob } from "@/server/queues/deployments-queue";
import { myQueue } from "@/server/queues/queueSetup";
import { eq } from "drizzle-orm";
import type { NextApiRequest, NextApiResponse } from "next";
-import { extractBranchName, extractCommitMessage } from "../[refreshToken]";
+import {
+ extractBranchName,
+ extractCommitMessage,
+ extractHash,
+} from "../[refreshToken]";
import { updateCompose } from "@/server/api/services/compose";
export default async function handler(
@@ -34,7 +38,7 @@ export default async function handler(
}
const deploymentTitle = extractCommitMessage(req.headers, req.body);
-
+ const deploymentHash = extractHash(req.headers, req.body);
const sourceType = composeResult.sourceType;
if (sourceType === "github") {
@@ -61,6 +65,7 @@ export default async function handler(
titleLog: deploymentTitle,
type: "deploy",
applicationType: "compose",
+ descriptionLog: `Hash: ${deploymentHash}`,
};
await myQueue.add(
"deployments",
diff --git a/pages/swagger.tsx b/pages/swagger.tsx
index 3db4e0c3b..715781531 100644
--- a/pages/swagger.tsx
+++ b/pages/swagger.tsx
@@ -6,15 +6,32 @@ import type { GetServerSidePropsContext, NextPage } from "next";
import dynamic from "next/dynamic";
import "swagger-ui-react/swagger-ui.css";
import superjson from "superjson";
+import { useEffect, useState } from "react";
const SwaggerUI = dynamic(() => import("swagger-ui-react"), { ssr: false });
const Home: NextPage = () => {
const { data } = api.settings.getOpenApiDocument.useQuery();
+ const [spec, setSpec] = useState({});
+
+ useEffect(() => {
+ // Esto solo se ejecutará en el cliente
+ if (data) {
+ const protocolAndHost = `${window.location.protocol}//${window.location.host}/api`;
+ const newSpec = {
+ ...data,
+ servers: [{ url: protocolAndHost }],
+ externalDocs: {
+ url: `${protocolAndHost}/settings.getOpenApiDocument`,
+ },
+ };
+ setSpec(newSpec);
+ }
+ }, [data]);
return (
-
+
);
};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f6f41788b..bd08da38b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -100,7 +100,7 @@ dependencies:
version: 10.45.2(@trpc/server@10.45.2)
'@trpc/next':
specifier: ^10.43.6
- version: 10.45.2(@tanstack/react-query@4.36.1)(@trpc/client@10.45.2)(@trpc/react-query@10.45.2)(@trpc/server@10.45.2)(next@14.2.3)(react-dom@18.2.0)(react@18.2.0)
+ version: 10.45.2(@tanstack/react-query@4.36.1)(@trpc/client@10.45.2)(@trpc/react-query@10.45.2)(@trpc/server@10.45.2)(next@14.2.4)(react-dom@18.2.0)(react@18.2.0)
'@trpc/react-query':
specifier: ^10.43.6
version: 10.45.2(@tanstack/react-query@4.36.1)(@trpc/client@10.45.2)(@trpc/server@10.45.2)(react-dom@18.2.0)(react@18.2.0)
@@ -193,10 +193,10 @@ dependencies:
version: 3.3.7
next:
specifier: ^14.1.3
- version: 14.2.3(react-dom@18.2.0)(react@18.2.0)
+ version: 14.2.4(react-dom@18.2.0)(react@18.2.0)
next-themes:
specifier: ^0.2.1
- version: 0.2.1(next@14.2.3)(react-dom@18.2.0)(react@18.2.0)
+ version: 0.2.1(next@14.2.4)(react-dom@18.2.0)(react@18.2.0)
node-os-utils:
specifier: 1.3.7
version: 1.3.7
@@ -2059,12 +2059,12 @@ packages:
dev: false
optional: true
- /@next/env@14.2.3:
- resolution: {integrity: sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==}
+ /@next/env@14.2.4:
+ resolution: {integrity: sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg==}
dev: false
- /@next/swc-darwin-arm64@14.2.3:
- resolution: {integrity: sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==}
+ /@next/swc-darwin-arm64@14.2.4:
+ resolution: {integrity: sha512-AH3mO4JlFUqsYcwFUHb1wAKlebHU/Hv2u2kb1pAuRanDZ7pD/A/KPD98RHZmwsJpdHQwfEc/06mgpSzwrJYnNg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
@@ -2072,8 +2072,8 @@ packages:
dev: false
optional: true
- /@next/swc-darwin-x64@14.2.3:
- resolution: {integrity: sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==}
+ /@next/swc-darwin-x64@14.2.4:
+ resolution: {integrity: sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
@@ -2081,48 +2081,44 @@ packages:
dev: false
optional: true
- /@next/swc-linux-arm64-gnu@14.2.3:
- resolution: {integrity: sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==}
+ /@next/swc-linux-arm64-gnu@14.2.4:
+ resolution: {integrity: sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
requiresBuild: true
dev: false
optional: true
- /@next/swc-linux-arm64-musl@14.2.3:
- resolution: {integrity: sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==}
+ /@next/swc-linux-arm64-musl@14.2.4:
+ resolution: {integrity: sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- libc: [musl]
requiresBuild: true
dev: false
optional: true
- /@next/swc-linux-x64-gnu@14.2.3:
- resolution: {integrity: sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==}
+ /@next/swc-linux-x64-gnu@14.2.4:
+ resolution: {integrity: sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [glibc]
requiresBuild: true
dev: false
optional: true
- /@next/swc-linux-x64-musl@14.2.3:
- resolution: {integrity: sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==}
+ /@next/swc-linux-x64-musl@14.2.4:
+ resolution: {integrity: sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- libc: [musl]
requiresBuild: true
dev: false
optional: true
- /@next/swc-win32-arm64-msvc@14.2.3:
- resolution: {integrity: sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==}
+ /@next/swc-win32-arm64-msvc@14.2.4:
+ resolution: {integrity: sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
@@ -2130,8 +2126,8 @@ packages:
dev: false
optional: true
- /@next/swc-win32-ia32-msvc@14.2.3:
- resolution: {integrity: sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==}
+ /@next/swc-win32-ia32-msvc@14.2.4:
+ resolution: {integrity: sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
@@ -2139,8 +2135,8 @@ packages:
dev: false
optional: true
- /@next/swc-win32-x64-msvc@14.2.3:
- resolution: {integrity: sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==}
+ /@next/swc-win32-x64-msvc@14.2.4:
+ resolution: {integrity: sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -4952,7 +4948,7 @@ packages:
'@trpc/server': 10.45.2
dev: false
- /@trpc/next@10.45.2(@tanstack/react-query@4.36.1)(@trpc/client@10.45.2)(@trpc/react-query@10.45.2)(@trpc/server@10.45.2)(next@14.2.3)(react-dom@18.2.0)(react@18.2.0):
+ /@trpc/next@10.45.2(@tanstack/react-query@4.36.1)(@trpc/client@10.45.2)(@trpc/react-query@10.45.2)(@trpc/server@10.45.2)(next@14.2.4)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-RSORmfC+/nXdmRY1pQ0AalsVgSzwNAFbZLYHiTvPM5QQ8wmMEHilseCYMXpu0se/TbPt9zVR6Ka2d7O6zxKkXg==}
peerDependencies:
'@tanstack/react-query': ^4.18.0
@@ -4967,7 +4963,7 @@ packages:
'@trpc/client': 10.45.2(@trpc/server@10.45.2)
'@trpc/react-query': 10.45.2(@tanstack/react-query@4.36.1)(@trpc/client@10.45.2)(@trpc/server@10.45.2)(react-dom@18.2.0)(react@18.2.0)
'@trpc/server': 10.45.2
- next: 14.2.3(react-dom@18.2.0)(react@18.2.0)
+ next: 14.2.4(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
@@ -8104,14 +8100,14 @@ packages:
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
dev: false
- /next-themes@0.2.1(next@14.2.3)(react-dom@18.2.0)(react@18.2.0):
+ /next-themes@0.2.1(next@14.2.4)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
peerDependencies:
next: '*'
react: '*'
react-dom: '*'
dependencies:
- next: 14.2.3(react-dom@18.2.0)(react@18.2.0)
+ next: 14.2.4(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
@@ -8120,8 +8116,8 @@ packages:
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
dev: true
- /next@14.2.3(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==}
+ /next@14.2.4(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ==}
engines: {node: '>=18.17.0'}
hasBin: true
peerDependencies:
@@ -8138,7 +8134,7 @@ packages:
sass:
optional: true
dependencies:
- '@next/env': 14.2.3
+ '@next/env': 14.2.4
'@swc/helpers': 0.5.5
busboy: 1.6.0
caniuse-lite: 1.0.30001598
@@ -8148,15 +8144,15 @@ packages:
react-dom: 18.2.0(react@18.2.0)
styled-jsx: 5.1.1(react@18.2.0)
optionalDependencies:
- '@next/swc-darwin-arm64': 14.2.3
- '@next/swc-darwin-x64': 14.2.3
- '@next/swc-linux-arm64-gnu': 14.2.3
- '@next/swc-linux-arm64-musl': 14.2.3
- '@next/swc-linux-x64-gnu': 14.2.3
- '@next/swc-linux-x64-musl': 14.2.3
- '@next/swc-win32-arm64-msvc': 14.2.3
- '@next/swc-win32-ia32-msvc': 14.2.3
- '@next/swc-win32-x64-msvc': 14.2.3
+ '@next/swc-darwin-arm64': 14.2.4
+ '@next/swc-darwin-x64': 14.2.4
+ '@next/swc-linux-arm64-gnu': 14.2.4
+ '@next/swc-linux-arm64-musl': 14.2.4
+ '@next/swc-linux-x64-gnu': 14.2.4
+ '@next/swc-linux-x64-musl': 14.2.4
+ '@next/swc-win32-arm64-msvc': 14.2.4
+ '@next/swc-win32-ia32-msvc': 14.2.4
+ '@next/swc-win32-x64-msvc': 14.2.4
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
@@ -8551,7 +8547,7 @@ packages:
dependencies:
nanoid: 3.3.7
picocolors: 1.0.0
- source-map-js: 1.0.2
+ source-map-js: 1.2.0
dev: false
/postcss@8.4.35:
@@ -9404,7 +9400,6 @@ packages:
/source-map-js@1.2.0:
resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
engines: {node: '>=0.10.0'}
- dev: true
/source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
diff --git a/server/api/routers/application.ts b/server/api/routers/application.ts
index df5f2b507..77b8befe6 100644
--- a/server/api/routers/application.ts
+++ b/server/api/routers/application.ts
@@ -162,6 +162,7 @@ export const applicationRouter = createTRPCRouter({
const jobData: DeploymentJob = {
applicationId: input.applicationId,
titleLog: "Rebuild deployment",
+ descriptionLog: "",
type: "redeploy",
applicationType: "application",
};
@@ -294,6 +295,7 @@ export const applicationRouter = createTRPCRouter({
const jobData: DeploymentJob = {
applicationId: input.applicationId,
titleLog: "Manual deployment",
+ descriptionLog: "",
type: "deploy",
applicationType: "application",
};
diff --git a/server/api/routers/compose.ts b/server/api/routers/compose.ts
index f23550b40..a722d17a9 100644
--- a/server/api/routers/compose.ts
+++ b/server/api/routers/compose.ts
@@ -138,6 +138,7 @@ export const composeRouter = createTRPCRouter({
titleLog: "Manual deployment",
type: "deploy",
applicationType: "compose",
+ descriptionLog: "",
};
await myQueue.add(
"deployments",
@@ -156,6 +157,7 @@ export const composeRouter = createTRPCRouter({
titleLog: "Rebuild deployment",
type: "redeploy",
applicationType: "compose",
+ descriptionLog: "",
};
await myQueue.add(
"deployments",
diff --git a/server/api/routers/domain.ts b/server/api/routers/domain.ts
index ace938c01..ec7f13d4f 100644
--- a/server/api/routers/domain.ts
+++ b/server/api/routers/domain.ts
@@ -12,6 +12,8 @@ import {
createDomain,
findDomainById,
findDomainsByApplicationId,
+ generateDomain,
+ generateWildcard,
removeDomainById,
updateDomainById,
} from "../services/domain";
@@ -35,6 +37,16 @@ export const domainRouter = createTRPCRouter({
.query(async ({ input }) => {
return await findDomainsByApplicationId(input.applicationId);
}),
+ generateDomain: protectedProcedure
+ .input(apiFindDomainByApplication)
+ .mutation(async ({ input }) => {
+ return generateDomain(input);
+ }),
+ generateWildcard: protectedProcedure
+ .input(apiFindDomainByApplication)
+ .mutation(async ({ input }) => {
+ return generateWildcard(input);
+ }),
update: protectedProcedure
.input(apiUpdateDomain)
.mutation(async ({ input }) => {
diff --git a/server/api/routers/settings.ts b/server/api/routers/settings.ts
index 7a222fe00..ac8eba3fb 100644
--- a/server/api/routers/settings.ts
+++ b/server/api/routers/settings.ts
@@ -248,7 +248,7 @@ export const settingsRouter = createTRPCRouter({
getOpenApiDocument: protectedProcedure.query(
async ({ ctx }): Promise => {
const protocol = ctx.req.headers["x-forwarded-proto"];
- const url = `${protocol}://${ctx.req.headers.host}/api/trpc`;
+ const url = `${protocol}://${ctx.req.headers.host}/api`;
const openApiDocument = generateOpenApiDocument(appRouter, {
title: "tRPC OpenAPI",
version: "1.0.0",
diff --git a/server/api/services/application.ts b/server/api/services/application.ts
index e5719a9a5..b093f63fb 100644
--- a/server/api/services/application.ts
+++ b/server/api/services/application.ts
@@ -130,15 +130,18 @@ export const updateApplicationStatus = async (
export const deployApplication = async ({
applicationId,
titleLog = "Manual deployment",
+ descriptionLog = "",
}: {
applicationId: string;
titleLog: string;
+ descriptionLog: string;
}) => {
const application = await findApplicationById(applicationId);
const admin = await findAdmin();
const deployment = await createDeployment({
applicationId: applicationId,
title: titleLog,
+ description: descriptionLog,
});
try {
@@ -173,14 +176,17 @@ export const deployApplication = async ({
export const rebuildApplication = async ({
applicationId,
titleLog = "Rebuild deployment",
+ descriptionLog = "",
}: {
applicationId: string;
titleLog: string;
+ descriptionLog: string;
}) => {
const application = await findApplicationById(applicationId);
const deployment = await createDeployment({
applicationId: applicationId,
title: titleLog,
+ description: descriptionLog,
});
try {
diff --git a/server/api/services/compose.ts b/server/api/services/compose.ts
index 63065e6ce..97d27e7ed 100644
--- a/server/api/services/compose.ts
+++ b/server/api/services/compose.ts
@@ -134,15 +134,18 @@ export const updateCompose = async (
export const deployCompose = async ({
composeId,
titleLog = "Manual deployment",
+ descriptionLog = "",
}: {
composeId: string;
titleLog: string;
+ descriptionLog: string;
}) => {
const compose = await findComposeById(composeId);
const admin = await findAdmin();
const deployment = await createDeploymentCompose({
composeId: composeId,
title: titleLog,
+ description: descriptionLog,
});
try {
@@ -170,14 +173,17 @@ export const deployCompose = async ({
export const rebuildCompose = async ({
composeId,
titleLog = "Rebuild deployment",
+ descriptionLog = "",
}: {
composeId: string;
titleLog: string;
+ descriptionLog: string;
}) => {
const compose = await findComposeById(composeId);
const deployment = await createDeploymentCompose({
composeId: composeId,
title: titleLog,
+ description: descriptionLog,
});
try {
diff --git a/server/api/services/deployment.ts b/server/api/services/deployment.ts
index b946fa7ac..e3bbad79f 100644
--- a/server/api/services/deployment.ts
+++ b/server/api/services/deployment.ts
@@ -60,6 +60,7 @@ export const createDeployment = async (
title: deployment.title || "Deployment",
status: "running",
logPath: logFilePath,
+ description: deployment.description || "",
})
.returning();
if (deploymentCreate.length === 0 || !deploymentCreate[0]) {
@@ -100,6 +101,7 @@ export const createDeploymentCompose = async (
.values({
composeId: deployment.composeId,
title: deployment.title || "Deployment",
+ description: deployment.description || "",
status: "running",
logPath: logFilePath,
})
diff --git a/server/api/services/domain.ts b/server/api/services/domain.ts
index 05a1cfa62..d7e4a271c 100644
--- a/server/api/services/domain.ts
+++ b/server/api/services/domain.ts
@@ -1,9 +1,15 @@
import { db } from "@/server/db";
-import { type apiCreateDomain, domains } from "@/server/db/schema";
+import {
+ type apiCreateDomain,
+ type apiFindDomainByApplication,
+ domains,
+} from "@/server/db/schema";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { findApplicationById } from "./application";
import { manageDomain } from "@/server/utils/traefik/domain";
+import { findAdmin } from "./admin";
+import { generateRandomDomain } from "@/templates/utils";
export type Domain = typeof domains.$inferSelect;
@@ -29,6 +35,58 @@ export const createDomain = async (input: typeof apiCreateDomain._type) => {
await manageDomain(application, domain);
});
};
+
+export const generateDomain = async (
+ input: typeof apiFindDomainByApplication._type,
+) => {
+ const application = await findApplicationById(input.applicationId);
+ const admin = await findAdmin();
+ const domain = await createDomain({
+ applicationId: application.applicationId,
+ host: generateRandomDomain({
+ serverIp: admin.serverIp || "",
+ projectName: application.appName,
+ }),
+ port: 3000,
+ certificateType: "none",
+ https: false,
+ path: "/",
+ });
+
+ return domain;
+};
+
+export const generateWildcard = async (
+ input: typeof apiFindDomainByApplication._type,
+) => {
+ const application = await findApplicationById(input.applicationId);
+ const admin = await findAdmin();
+
+ if (!admin.host) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "We need a host to generate a wildcard domain",
+ });
+ }
+ const domain = await createDomain({
+ applicationId: application.applicationId,
+ host: generateWildcardDomain(application.appName, admin.host || ""),
+ port: 3000,
+ certificateType: "none",
+ https: false,
+ path: "/",
+ });
+
+ return domain;
+};
+
+export const generateWildcardDomain = (
+ appName: string,
+ serverDomain: string,
+) => {
+ return `${appName}-${serverDomain}`;
+};
+
export const findDomainById = async (domainId: string) => {
const domain = await db.query.domains.findFirst({
where: eq(domains.domainId, domainId),
diff --git a/server/db/schema/deployment.ts b/server/db/schema/deployment.ts
index eaadc695d..58931d6de 100644
--- a/server/db/schema/deployment.ts
+++ b/server/db/schema/deployment.ts
@@ -18,6 +18,7 @@ export const deployments = pgTable("deployment", {
.primaryKey()
.$defaultFn(() => nanoid()),
title: text("title").notNull(),
+ description: text("description"),
status: deploymentStatus("status").default("running"),
logPath: text("logPath").notNull(),
applicationId: text("applicationId").references(
@@ -49,6 +50,7 @@ const schema = createInsertSchema(deployments, {
logPath: z.string().min(1),
applicationId: z.string(),
composeId: z.string(),
+ description: z.string().optional(),
});
export const apiCreateDeployment = schema
@@ -57,6 +59,7 @@ export const apiCreateDeployment = schema
status: true,
logPath: true,
applicationId: true,
+ description: true,
})
.extend({
applicationId: z.string().min(1),
@@ -68,6 +71,7 @@ export const apiCreateDeploymentCompose = schema
status: true,
logPath: true,
composeId: true,
+ description: true,
})
.extend({
composeId: z.string().min(1),
diff --git a/server/queues/deployments-queue.ts b/server/queues/deployments-queue.ts
index ef909d8e8..54f23073b 100644
--- a/server/queues/deployments-queue.ts
+++ b/server/queues/deployments-queue.ts
@@ -10,12 +10,14 @@ type DeployJob =
| {
applicationId: string;
titleLog: string;
+ descriptionLog: string;
type: "deploy" | "redeploy";
applicationType: "application";
}
| {
composeId: string;
titleLog: string;
+ descriptionLog: string;
type: "deploy" | "redeploy";
applicationType: "compose";
};
@@ -31,11 +33,13 @@ export const deploymentWorker = new Worker(
await rebuildApplication({
applicationId: job.data.applicationId,
titleLog: job.data.titleLog,
+ descriptionLog: job.data.descriptionLog,
});
} else if (job.data.type === "deploy") {
await deployApplication({
applicationId: job.data.applicationId,
titleLog: job.data.titleLog,
+ descriptionLog: job.data.descriptionLog,
});
}
} else if (job.data.applicationType === "compose") {
@@ -43,11 +47,13 @@ export const deploymentWorker = new Worker(
await deployCompose({
composeId: job.data.composeId,
titleLog: job.data.titleLog,
+ descriptionLog: job.data.descriptionLog,
});
} else if (job.data.type === "redeploy") {
await rebuildCompose({
composeId: job.data.composeId,
titleLog: job.data.titleLog,
+ descriptionLog: job.data.descriptionLog,
});
}
}
diff --git a/server/utils/databases/mariadb.ts b/server/utils/databases/mariadb.ts
index 34ea2c8c7..cd7c7e4ad 100644
--- a/server/utils/databases/mariadb.ts
+++ b/server/utils/databases/mariadb.ts
@@ -63,6 +63,9 @@ export const buildMariadb = async (mariadb: MariadbWithMounts) => {
Resources: {
...resources,
},
+ Placement: {
+ Constraints: ["node.role==manager"],
+ },
},
Mode: {
Replicated: {
diff --git a/server/utils/databases/mongo.ts b/server/utils/databases/mongo.ts
index c34824925..5c07bab4a 100644
--- a/server/utils/databases/mongo.ts
+++ b/server/utils/databases/mongo.ts
@@ -63,6 +63,9 @@ export const buildMongo = async (mongo: MongoWithMounts) => {
Resources: {
...resources,
},
+ Placement: {
+ Constraints: ["node.role==manager"],
+ },
},
Mode: {
Replicated: {
diff --git a/server/utils/databases/mysql.ts b/server/utils/databases/mysql.ts
index ec529241a..8c5bbce4c 100644
--- a/server/utils/databases/mysql.ts
+++ b/server/utils/databases/mysql.ts
@@ -69,6 +69,9 @@ export const buildMysql = async (mysql: MysqlWithMounts) => {
Resources: {
...resources,
},
+ Placement: {
+ Constraints: ["node.role==manager"],
+ },
},
Mode: {
Replicated: {
diff --git a/server/utils/databases/postgres.ts b/server/utils/databases/postgres.ts
index b2cbfb674..ef8857acb 100644
--- a/server/utils/databases/postgres.ts
+++ b/server/utils/databases/postgres.ts
@@ -63,6 +63,9 @@ export const buildPostgres = async (postgres: PostgresWithMounts) => {
Resources: {
...resources,
},
+ Placement: {
+ Constraints: ["node.role==manager"],
+ },
},
Mode: {
Replicated: {
diff --git a/server/utils/databases/redis.ts b/server/utils/databases/redis.ts
index a3e3d5933..76125ad7e 100644
--- a/server/utils/databases/redis.ts
+++ b/server/utils/databases/redis.ts
@@ -50,17 +50,19 @@ export const buildRedis = async (redis: RedisWithMounts) => {
Image: dockerImage,
Env: envVariables,
Mounts: [...volumesMount, ...bindsMount, ...filesMount],
- ...(command
- ? {
- Command: ["/bin/sh"],
- Args: ["-c", command],
- }
- : {}),
+ Command: ["/bin/sh"],
+ Args: [
+ "-c",
+ command ? command : `redis-server --requirepass ${databasePassword}`,
+ ],
},
Networks: [{ Target: "dokploy-network" }],
Resources: {
...resources,
},
+ Placement: {
+ Constraints: ["node.role==manager"],
+ },
},
Mode: {
Replicated: {