diff --git a/apps/docs/app/[lang]/[[...slug]]/page.tsx b/apps/docs/app/[lang]/[[...slug]]/page.tsx index 9a7f3d2cb..8307d655d 100644 --- a/apps/docs/app/[lang]/[[...slug]]/page.tsx +++ b/apps/docs/app/[lang]/[[...slug]]/page.tsx @@ -74,7 +74,7 @@ export function generateMetadata({ }, twitter: { card: "summary_large_image", - creator: "@siumauricio", + creator: "@getdokploy", title: page.data.title, description: page.data.description, images: [ diff --git a/apps/docs/content/docs/core/application/overview.mdx b/apps/docs/content/docs/core/application/overview.mdx index 788daff82..9f881f6b8 100644 --- a/apps/docs/content/docs/core/application/overview.mdx +++ b/apps/docs/content/docs/core/application/overview.mdx @@ -15,6 +15,8 @@ Configure the source of your code, the way your application is built, and also m If you need to assign environment variables to your application, you can do so here. +In case you need to use a multiline variable, you can wrap it in double quotes just like this `'"here_is_my_private_key"'`. + ## Monitoring Four graphs will be displayed for the use of memory, CPU, disk, and network. Note that the information is only updated if you are viewing the current page, otherwise it will not be updated. diff --git a/apps/docs/content/docs/core/databases/overview.mdx b/apps/docs/content/docs/core/databases/overview.mdx index f9702fb01..0fd2f5b0d 100644 --- a/apps/docs/content/docs/core/databases/overview.mdx +++ b/apps/docs/content/docs/core/databases/overview.mdx @@ -26,6 +26,8 @@ Actions like deploying, updating, and deleting your database, and stopping it. If you need to assign environment variables to your application, you can do so here. +In case you need to use a multiline variable, you can wrap it in double quotes just like this `'"here_is_my_private_key"'`. + ## Monitoring Four graphs will be displayed for the use of memory, CPU, disk, and network. Note that the information is only updated if you are viewing the current page, otherwise it will not be updated. diff --git a/apps/dokploy/components/dashboard/application/advanced/redirects/add-redirect.tsx b/apps/dokploy/components/dashboard/application/advanced/redirects/add-redirect.tsx index efe1b9ac0..320471ba5 100644 --- a/apps/dokploy/components/dashboard/application/advanced/redirects/add-redirect.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/redirects/add-redirect.tsx @@ -19,6 +19,15 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Separator } from "@/components/ui/separator"; import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -36,6 +45,36 @@ const AddRedirectchema = z.object({ type AddRedirect = z.infer; +// Default presets +const redirectPresets = [ + // { + // label: "Allow www & non-www.", + // redirect: { + // regex: "", + // permanent: false, + // replacement: "", + // }, + // }, + { + id: "to-www", + label: "Redirect to www", + redirect: { + regex: "^https?://(?:www.)?(.+)", + permanent: true, + replacement: "https://www.$${1}", + }, + }, + { + id: "to-non-www", + label: "Redirect to non-www", + redirect: { + regex: "^https?://www.(.+)", + permanent: true, + replacement: "https://$${1}", + }, + }, +]; + interface Props { applicationId: string; children?: React.ReactNode; @@ -43,9 +82,10 @@ interface Props { export const AddRedirect = ({ applicationId, - children = , + children = , }: Props) => { const [isOpen, setIsOpen] = useState(false); + const [presetSelected, setPresetSelected] = useState(""); const utils = api.useUtils(); const { mutateAsync, isLoading, error, isError } = @@ -81,19 +121,36 @@ export const AddRedirect = ({ await utils.application.readTraefikConfig.invalidate({ applicationId, }); - setIsOpen(false); + onDialogToggle(false); }) .catch(() => { toast.error("Error to create the redirect"); }); }; + const onDialogToggle = (open: boolean) => { + setIsOpen(open); + // commented for the moment because not reseting the form if accidentally closed the dialog can be considered as a feature instead of a bug + // setPresetSelected(""); + // form.reset(); + }; + + const onPresetSelect = (presetId: string) => { + const redirectPreset = redirectPresets.find( + (preset) => preset.id === presetId, + )?.redirect; + if (!redirectPreset) return; + const { regex, permanent, replacement } = redirectPreset; + form.reset({ regex, permanent, replacement }, { keepDefaultValues: true }); + setPresetSelected(presetId); + }; + return ( - + - + Redirects @@ -102,6 +159,24 @@ export const AddRedirect = ({ {isError && {error?.message}} +
+ + +
+ + +
( - +
Permanent diff --git a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx index 43a3cb69c..7ab67a29d 100644 --- a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx @@ -140,7 +140,7 @@ export const AddDomain = ({ {children} - + Domain {dictionary.dialogDescription} @@ -241,6 +241,29 @@ export const AddDomain = ({ ); }} /> + + ( + +
+ HTTPS + + Automatically provision SSL Certificate. + + +
+ + + +
+ )} + /> + {form.getValues().https && ( )} - - ( - -
- HTTPS - - Automatically provision SSL Certificate. - - -
- - - -
- )} - />
diff --git a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx index 056c003ae..91f211d14 100644 --- a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx @@ -161,7 +161,7 @@ export const AddDomainCompose = ({ {children} - + Domain {dictionary.dialogDescription} @@ -190,7 +190,7 @@ export const AddDomainCompose = ({ {errorServices?.message} )} -
+
+ + ( + +
+ HTTPS + + Automatically provision SSL Certificate. + + +
+ + + +
+ )} + /> + {https && ( )} - - ( - -
- HTTPS - - Automatically provision SSL Certificate. - - -
- - - -
- )} - />
diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 023218997..f5b576354 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.9.0", + "version": "v0.9.1", "private": true, "license": "Apache-2.0", "type": "module", diff --git a/apps/dokploy/server/auth/token.ts b/apps/dokploy/server/auth/token.ts index 54162fdcb..5581777ae 100644 --- a/apps/dokploy/server/auth/token.ts +++ b/apps/dokploy/server/auth/token.ts @@ -1,6 +1,8 @@ import type { IncomingMessage } from "node:http"; import { TimeSpan } from "lucia"; import { Lucia } from "lucia/dist/core.js"; +import { findAdminByAuthId } from "../api/services/admin"; +import { findUserByAuthId } from "../api/services/user"; import { type ReturnValidateToken, adapter } from "./auth"; export const luciaToken = new Lucia(adapter, { @@ -31,6 +33,16 @@ export const validateBearerToken = async ( }; } const result = await luciaToken.validateSession(sessionId); + + if (result.user) { + if (result.user?.rol === "admin") { + const admin = await findAdminByAuthId(result.user.id); + result.user.adminId = admin.adminId; + } else if (result.user?.rol === "user") { + const userResult = await findUserByAuthId(result.user.id); + result.user.adminId = userResult.adminId; + } + } return { session: result.session, ...((result.user && { diff --git a/apps/dokploy/server/utils/builders/nixpacks.ts b/apps/dokploy/server/utils/builders/nixpacks.ts index 0f408743c..2d81a7c01 100644 --- a/apps/dokploy/server/utils/builders/nixpacks.ts +++ b/apps/dokploy/server/utils/builders/nixpacks.ts @@ -1,4 +1,4 @@ -import type { WriteStream } from "node:fs"; +import { type WriteStream, existsSync, mkdirSync } from "node:fs"; import path from "node:path"; import { buildStatic, getStaticCommand } from "@/server/utils/builders/static"; import { nanoid } from "nanoid"; @@ -42,7 +42,6 @@ export const buildNixpacks = async ( and copy the artifacts on the host filesystem. Then, remove the container and create a static build. */ - if (publishDirectory) { await spawnAsync( "docker", @@ -50,12 +49,22 @@ export const buildNixpacks = async ( writeToStream, ); + const localPath = path.join(buildAppDirectory, publishDirectory); + + if (!existsSync(path.dirname(localPath))) { + mkdirSync(path.dirname(localPath), { recursive: true }); + } + + // https://docs.docker.com/reference/cli/docker/container/cp/ + const isDirectory = + publishDirectory.endsWith("/") || !path.extname(publishDirectory); + await spawnAsync( "docker", [ "cp", - `${buildContainerId}:/app/${publishDirectory}`, - path.join(buildAppDirectory, publishDirectory), + `${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""}`, + localPath, ], writeToStream, ); @@ -108,9 +117,14 @@ echo "✅ Nixpacks build completed." >> ${logPath}; Then, remove the container and create a static build. */ if (publishDirectory) { + const localPath = path.join(buildAppDirectory, publishDirectory); + const isDirectory = + publishDirectory.endsWith("/") || !path.extname(publishDirectory); + bashCommand += ` docker create --name ${buildContainerId} ${appName} -docker cp ${buildContainerId}:/app/${publishDirectory} ${path.join(buildAppDirectory, publishDirectory)} >> ${logPath} 2>> ${logPath} || { +mkdir -p ${localPath} +docker cp ${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""} ${path.join(buildAppDirectory, publishDirectory)} >> ${logPath} 2>> ${logPath} || { docker rm ${buildContainerId} echo "❌ Copying ${publishDirectory} to ${path.join(buildAppDirectory, publishDirectory)} failed" >> ${logPath}; exit 1; diff --git a/apps/website/components/Footer.tsx b/apps/website/components/Footer.tsx index c1235add3..88ac33a5a 100644 --- a/apps/website/components/Footer.tsx +++ b/apps/website/components/Footer.tsx @@ -33,7 +33,7 @@ export function Footer() {