diff --git a/apps/docs/app/[lang]/[[...slug]]/page.tsx b/apps/docs/app/[lang]/[[...slug]]/page.tsx index 9a7f3d2c..8307d655 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 788daff8..9f881f6b 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 f9702fb0..0fd2f5b0 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 efe1b9ac..320471ba 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 43a3cb69..7ab67a29 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 056c003a..91f211d1 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 02321899..f5b57635 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 54162fdc..5581777a 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 0f408743..2d81a7c0 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 c1235add..88ac33a5 100644 --- a/apps/website/components/Footer.tsx +++ b/apps/website/components/Footer.tsx @@ -33,7 +33,7 @@ export function Footer() {