Compare commits

...

51 Commits

Author SHA1 Message Date
Mauricio Siu
17e9154887 Merge pull request #2257 from Dokploy/fix/send-build-error-on-remote-servers
Fix/send build error on remote servers
2025-07-28 01:52:57 -06:00
Mauricio Siu
2442494096 fix(application): simplify error message handling in deployment notifications 2025-07-28 01:51:21 -06:00
Mauricio Siu
bac2afb423 refactor(application): exclude appName from updateApplication data to streamline database updates 2025-07-28 01:50:58 -06:00
Mauricio Siu
4e9630e976 Merge pull request #2256 from Dokploy/feat/enhancements-cloud-version-ui
feat(dashboard): enhance application and database forms with tooltips…
2025-07-28 01:50:26 -06:00
Mauricio Siu
558f6aecae fix(application): improve error handling and notification messages during deployment 2025-07-28 01:48:33 -06:00
Mauricio Siu
c3e2b0d0f1 feat(dashboard): enhance application and database forms with tooltips for better user guidance 2025-07-28 01:12:43 -06:00
Mauricio Siu
11d584316a chore(package): bump version to v0.24.5 2025-07-28 00:57:44 -06:00
Mauricio Siu
f78dc555b2 Merge pull request #2244 from jhon2c/feat/improve-server-ux
feat(ux): Improve UX Based on Community Feedback
2025-07-27 23:21:24 -06:00
Mauricio Siu
5812b12a59 Merge pull request #2236 from masesisaac/canary
fix(dashboard): Update app security view to hide password
2025-07-27 23:16:07 -06:00
Mauricio Siu
7301d15e8f Merge pull request #2230 from amustapha/patch-1
fix: wrap user prompt in ai modal to prevent text stretch
2025-07-27 23:15:01 -06:00
Mauricio Siu
f79796a6c8 Merge pull request #2188 from Marukome0743/vscode
chore: add biome settings for vscode editor
2025-07-27 23:14:33 -06:00
Mauricio Siu
4122b37abd Merge pull request #2250 from Dokploy/feat/add-name-field-to-profile
feat(profile): add optional name field to user profile form and schema
2025-07-27 23:13:26 -06:00
Mauricio Siu
79e9593663 feat(profile): add optional name field to user profile form and schema 2025-07-27 23:13:06 -06:00
masesisaac
def3fa0030 fix(security): change password input type to 'password' 2025-07-28 04:58:43 +03:00
autofix-ci[bot]
d561068bcd [autofix.ci] apply automated fixes 2025-07-26 17:26:20 +00:00
Jhon
212c1b2d5f feat(dashboard): show "Action Required" badge for incomplete Git provider setup 2025-07-26 14:18:26 -03:00
Jhon
d3a54172b5 feat(ux): add conditional server selection functionality to application forms 2025-07-26 13:53:28 -03:00
masesisaac
cda33eb291 refactor(dashboard): reorder imports in show-security.tsx for consistency 2025-07-24 17:45:26 +03:00
masesisaac
c178234e53 fix(dashboard): hide basic auth password by default 2025-07-24 17:41:51 +03:00
Abdulhakeem Adetunji Mustapha
329db1fd1a fix: wrap user prompt in ai modal to prevent text stretch 2025-07-23 19:30:47 +01:00
Marukome0743
6efbf030a7 chore: add biome settings for vscode editor 2025-07-23 08:49:59 +09:00
Mauricio Siu
b95dfed8fc chore(package): bump version to v0.24.4 2025-07-20 20:06:47 -06:00
Mauricio Siu
7fe3418d55 Merge pull request #2218 from Dokploy/2179-reloading-traefik-on-the-remote-server-will-cause-traefik-on-the-instance-to-change-accordingly
fix(traefik): remove duplicate file write operation in writeTraefikCo…
2025-07-20 20:05:48 -06:00
Mauricio Siu
288d86c73b fix(traefik): remove duplicate file write operation in writeTraefikConfigInPath function 2025-07-20 20:05:30 -06:00
Mauricio Siu
ffd5ccd386 Merge pull request #2202 from gentslava/feat/traefik-config
feat(config): Traefik
2025-07-20 19:45:53 -06:00
Mauricio Siu
98ddd096e5 Update packages/server/src/setup/traefik-setup.ts 2025-07-20 19:45:41 -06:00
Mauricio Siu
da6cc9fe72 Merge pull request #2190 from Marukome0743/format
chore: version up format.yml actions
2025-07-20 19:44:20 -06:00
Mauricio Siu
22d0af269e Merge pull request #2200 from Marukome0743/server
refactor: lint and sort imports on dokploy/server
2025-07-20 19:42:15 -06:00
Mauricio Siu
f0fdc46de5 Merge pull request #2187 from Marukome0743/v2
chore: upgrade to Biome v2
2025-07-20 19:41:49 -06:00
Mauricio Siu
9aea24115d Merge pull request #2199 from Marukome0743/lint
refactor: lint and sort import on dokploy application
2025-07-20 19:41:02 -06:00
Mauricio Siu
a9ee6c2393 Merge pull request #2194 from Marukome0743/pnpm
chore(package): version up pnpm to v9.12.0
2025-07-20 19:40:09 -06:00
Mauricio Siu
349717044c Merge pull request #2196 from Marukome0743/dispatch
ci: remove custom branch and add workflow_dispatch event
2025-07-20 19:37:27 -06:00
Mauricio Siu
f94f32695f Merge pull request #2195 from Marukome0743/monitoring
chore: remove `apps/monitoring` from `pnpm-workspace.yaml`
2025-07-20 19:37:07 -06:00
Mauricio Siu
37b78ea09c Merge pull request #2217 from Dokploy/2201-daily-docker-cleanup-not-working-on-remote-server
fix(dashboard): update Docker cleanup toggle logic to prioritize serv…
2025-07-20 19:01:46 -06:00
Mauricio Siu
9b89b4631f fix(dashboard): update Docker cleanup toggle logic to prioritize server settings 2025-07-20 19:01:20 -06:00
Mauricio Siu
7100095f2b Merge pull request #2216 from Dokploy/2209-update-s3-destination-form-loses-its-state-when-current-tab-loses-its-focus
fix(dashboard): disable refetch on window focus for destination handling
2025-07-20 18:57:33 -06:00
Mauricio Siu
a36ab65aa6 fix(dashboard): disable refetch on window focus for destination handling 2025-07-20 18:56:35 -06:00
Mauricio Siu
bf81ba20ff Merge pull request #2215 from Dokploy/2197-git-provider-api-undefined_value-error
refactor(auth): simplify user session structure in validateRequest fu…
2025-07-20 18:55:16 -06:00
Mauricio Siu
658a4a9b99 refactor(auth): simplify user session structure in validateRequest function
- Changed user object in mockSession to only include userId, removing email and name for a more streamlined session representation.
2025-07-20 18:54:57 -06:00
Mauricio Siu
47cb096cf3 Merge pull request #2214 from Dokploy/2203-identical-webhook-redeploy-url-after-duplicating-project
feat(project): add refreshToken to application and compose data retri…
2025-07-20 18:45:39 -06:00
Mauricio Siu
f3856722da feat(project): add refreshToken to application and compose data retrieval
- Included refreshToken in the data returned from findApplicationById and findComposeById functions to enhance application state management.
2025-07-20 18:45:18 -06:00
Vyacheslav Scherbinin
a67c3eb979 feat(conf): accessLog filePath 2025-07-16 16:46:47 +07:00
Vyacheslav Scherbinin
aaa205f104 feat(conf): disable sendAnonymousUsage 2025-07-16 12:29:31 +07:00
Marukome0743
cadea7ff28 refactor: lint and sort imports on dokploy/server 2025-07-15 14:22:37 +09:00
Marukome0743
9ab937f726 refactor: lint dokploy application 2025-07-15 14:13:32 +09:00
Marukome0743
d0af517eb7 ci: remove custom branch and add workflow_dispatch event 2025-07-14 19:03:41 +09:00
Marukome0743
66bdf9bf0a chore: remove apps/monitoring from pnpm-workspace.yaml 2025-07-14 18:24:26 +09:00
Marukome0743
d4a3af475a chore(package): version up pnpm to v9.12.0 2025-07-14 15:58:20 +09:00
autofix-ci[bot]
e92a8d7c98 [autofix.ci] apply automated fixes 2025-07-14 15:30:24 +09:00
Marukome0743
c4fec8cee5 chore: upgrade to Biome v2 2025-07-14 15:30:23 +09:00
Marukome0743
55f75bce53 chore: version up format.yml actions 2025-07-14 15:30:06 +09:00
63 changed files with 708 additions and 558 deletions

View File

@@ -2,7 +2,8 @@ name: Build Docker images
on:
push:
branches: ["canary", "main", "feat/monitoring"]
branches: [main, canary]
workflow_dispatch:
jobs:
build-and-push-cloud-image:

View File

@@ -2,7 +2,8 @@ name: Dokploy Docker Build
on:
push:
branches: [main, canary, "1061-custom-docker-service-hostname"]
branches: [main, canary]
workflow_dispatch:
env:
IMAGE_NAME: dokploy/dokploy

View File

@@ -11,12 +11,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup biomeJs
uses: biomejs/setup-biome@v2
- name: Run Biome formatter
run: biome format . --write
run: biome format --write
- uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef
- uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27 # v1.3.2

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["biomejs.biome"]
}

8
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "biomejs.biome",
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
}
}

View File

@@ -29,5 +29,9 @@
"tsx": "^4.16.2",
"typescript": "^5.8.3"
},
"packageManager": "pnpm@9.5.0"
"packageManager": "pnpm@9.12.0",
"engines": {
"node": "^20.16.0",
"pnpm": ">=9.12.0"
}
}

View File

@@ -151,7 +151,7 @@ export const HandleSecurity = ({
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input placeholder="test" {...field} />
<Input placeholder="test" type="password" {...field} />
</FormControl>
<FormMessage />

View File

@@ -7,6 +7,9 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { ToggleVisibilityInput } from "@/components/shared/toggle-visibility-input";
import { Input } from "@/components/ui/input";
import { api } from "@/utils/api";
import { LockKeyhole, Trash2 } from "lucide-react";
import { toast } from "sonner";
@@ -58,19 +61,18 @@ export const ShowSecurity = ({ applicationId }: Props) => {
<div className="flex flex-col gap-6 ">
{data?.security.map((security) => (
<div key={security.securityId}>
<div className="flex w-full flex-col sm:flex-row justify-between sm:items-center gap-4 sm:gap-10 border rounded-lg p-4">
<div className="grid grid-cols-1 sm:grid-cols-2 flex-col gap-4 sm:gap-8">
<div className="flex flex-col gap-1">
<span className="font-medium">Username</span>
<span className="text-sm text-muted-foreground">
{security.username}
</span>
<div className="flex w-full flex-col md:flex-row justify-between md:items-center gap-4 md:gap-10 border rounded-lg p-4">
<div className="grid grid-cols-1 md:grid-cols-2 flex-col gap-4 md:gap-8">
<div className="flex flex-col gap-2">
<Label>Username</Label>
<Input disabled value={security.username} />
</div>
<div className="flex flex-col gap-1">
<span className="font-medium">Password</span>
<span className="text-sm text-muted-foreground">
{security.password}
</span>
<div className="flex flex-col gap-2">
<Label>Password</Label>
<ToggleVisibilityInput
value={security.password}
disabled
/>
</div>
</div>
<div className="flex flex-row gap-2">

View File

@@ -1,4 +1,5 @@
import { DateTooltip } from "@/components/shared/date-tooltip";
import { DialogAction } from "@/components/shared/dialog-action";
import { StatusTooltip } from "@/components/shared/status-tooltip";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@@ -10,14 +11,13 @@ import {
CardTitle,
} from "@/components/ui/card";
import { type RouterOutputs, api } from "@/utils/api";
import { Clock, Loader2, RocketIcon, Settings, RefreshCcw } from "lucide-react";
import { Clock, Loader2, RefreshCcw, RocketIcon, Settings } from "lucide-react";
import React, { useEffect, useState } from "react";
import { toast } from "sonner";
import { ShowRollbackSettings } from "../rollbacks/show-rollback-settings";
import { CancelQueues } from "./cancel-queues";
import { RefreshToken } from "./refresh-token";
import { ShowDeployment } from "./show-deployment";
import { ShowRollbackSettings } from "../rollbacks/show-rollback-settings";
import { DialogAction } from "@/components/shared/dialog-action";
import { toast } from "sonner";
interface Props {
id: string;

View File

@@ -1,3 +1,4 @@
import { AlertBlock } from "@/components/shared/alert-block";
import { DrawerLogs } from "@/components/shared/drawer-logs";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@@ -42,9 +43,8 @@ import { useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { type LogLine, parseLogs } from "../../docker/logs/utils";
import { formatBytes } from "../../database/backups/restore-backup";
import { AlertBlock } from "@/components/shared/alert-block";
import { type LogLine, parseLogs } from "../../docker/logs/utils";
interface Props {
id: string;

View File

@@ -23,8 +23,8 @@ import {
Trash2,
} from "lucide-react";
import { toast } from "sonner";
import { HandleVolumeBackups } from "./handle-volume-backups";
import { ShowDeploymentsModal } from "../deployments/show-deployments-modal";
import { HandleVolumeBackups } from "./handle-volume-backups";
import { RestoreVolumeBackups } from "./restore-volume-backups";
interface Props {

View File

@@ -1,3 +1,4 @@
import { UnauthorizedGitProvider } from "@/components/dashboard/application/general/generic/unauthorized-git-provider";
import {
BitbucketIcon,
GitIcon,
@@ -11,6 +12,7 @@ import { api } from "@/utils/api";
import { CodeIcon, GitBranch, Loader2 } from "lucide-react";
import Link from "next/link";
import { useState } from "react";
import { toast } from "sonner";
import { ComposeFileEditor } from "../compose-file-editor";
import { ShowConvertedCompose } from "../show-converted-compose";
import { SaveBitbucketProviderCompose } from "./save-bitbucket-provider-compose";
@@ -18,8 +20,6 @@ import { SaveGitProviderCompose } from "./save-git-provider-compose";
import { SaveGiteaProviderCompose } from "./save-gitea-provider-compose";
import { SaveGithubProviderCompose } from "./save-github-provider-compose";
import { SaveGitlabProviderCompose } from "./save-gitlab-provider-compose";
import { UnauthorizedGitProvider } from "@/components/dashboard/application/general/generic/unauthorized-git-provider";
import { toast } from "sonner";
type TabState = "github" | "git" | "raw" | "gitlab" | "bitbucket" | "gitea";
interface Props {

View File

@@ -1,3 +1,9 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { Folder, HelpCircle } from "lucide-react";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
@@ -37,12 +43,6 @@ import {
} from "@/components/ui/tooltip";
import { slugify } from "@/lib/slug";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { Folder, HelpCircle } from "lucide-react";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const AddTemplateSchema = z.object({
name: z.string().min(1, {
@@ -75,6 +75,8 @@ export const AddApplication = ({ projectId, projectName }: Props) => {
const slug = slugify(projectName);
const { data: servers } = api.server.withSSHKey.useQuery();
const hasServers = servers && servers.length > 0;
const { mutateAsync, isLoading, error, isError } =
api.application.create.useMutation();
@@ -155,68 +157,84 @@ export const AddApplication = ({ projectId, projectName }: Props) => {
</FormItem>
)}
/>
<FormField
control={form.control}
name="serverId"
render={({ field }) => (
<FormItem>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<FormLabel className="break-all w-fit flex flex-row gap-1 items-center">
Select a Server {!isCloud ? "(Optional)" : ""}
<HelpCircle className="size-4 text-muted-foreground" />
</FormLabel>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
If no server is selected, the application will be
deployed on the server where the user is logged in.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
{hasServers && (
<FormField
control={form.control}
name="serverId"
render={({ field }) => (
<FormItem>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<FormLabel className="break-all w-fit flex flex-row gap-1 items-center">
Select a Server {!isCloud ? "(Optional)" : ""}
<HelpCircle className="size-4 text-muted-foreground" />
</FormLabel>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
If no server is selected, the application will be
deployed on the server where the user is logged in.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<SelectTrigger>
<SelectValue placeholder="Select a Server" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{servers?.map((server) => (
<SelectItem
key={server.serverId}
value={server.serverId}
>
<span className="flex items-center gap-2 justify-between w-full">
<span>{server.name}</span>
<span className="text-muted-foreground text-xs self-center">
{server.ipAddress}
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<SelectTrigger>
<SelectValue placeholder="Select a Server" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{servers?.map((server) => (
<SelectItem
key={server.serverId}
value={server.serverId}
>
<span className="flex items-center gap-2 justify-between w-full">
<span>{server.name}</span>
<span className="text-muted-foreground text-xs self-center">
{server.ipAddress}
</span>
</span>
</span>
</SelectItem>
))}
<SelectLabel>Servers ({servers?.length})</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</SelectItem>
))}
<SelectLabel>Servers ({servers?.length})</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
)}
<FormField
control={form.control}
name="appName"
render={({ field }) => (
<FormItem>
<FormLabel>App Name</FormLabel>
<FormLabel className="flex items-center gap-2">
App Name
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4 text-muted-foreground" />
</TooltipTrigger>
<TooltipContent side="right">
<p>
This will be the name of the Docker Swarm service
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</FormLabel>
<FormControl>
<Input placeholder="my-app" {...field} />
</FormControl>

View File

@@ -1,3 +1,9 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { CircuitBoard, HelpCircle } from "lucide-react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
@@ -37,12 +43,6 @@ import {
} from "@/components/ui/tooltip";
import { slugify } from "@/lib/slug";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { CircuitBoard, HelpCircle } from "lucide-react";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const AddComposeSchema = z.object({
composeType: z.enum(["docker-compose", "stack"]).optional(),
@@ -78,6 +78,8 @@ export const AddCompose = ({ projectId, projectName }: Props) => {
const { mutateAsync, isLoading, error, isError } =
api.compose.create.useMutation();
const hasServers = servers && servers.length > 0;
const form = useForm<AddCompose>({
defaultValues: {
name: "",
@@ -163,62 +165,64 @@ export const AddCompose = ({ projectId, projectName }: Props) => {
)}
/>
</div>
<FormField
control={form.control}
name="serverId"
render={({ field }) => (
<FormItem>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<FormLabel className="break-all w-fit flex flex-row gap-1 items-center">
Select a Server {!isCloud ? "(Optional)" : ""}
<HelpCircle className="size-4 text-muted-foreground" />
</FormLabel>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
If no server is selected, the application will be
deployed on the server where the user is logged in.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
{hasServers && (
<FormField
control={form.control}
name="serverId"
render={({ field }) => (
<FormItem>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<FormLabel className="break-all w-fit flex flex-row gap-1 items-center">
Select a Server {!isCloud ? "(Optional)" : ""}
<HelpCircle className="size-4 text-muted-foreground" />
</FormLabel>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
If no server is selected, the application will be
deployed on the server where the user is logged in.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<SelectTrigger>
<SelectValue placeholder="Select a Server" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{servers?.map((server) => (
<SelectItem
key={server.serverId}
value={server.serverId}
>
<span className="flex items-center gap-2 justify-between w-full">
<span>{server.name}</span>
<span className="text-muted-foreground text-xs self-center">
{server.ipAddress}
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<SelectTrigger>
<SelectValue placeholder="Select a Server" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{servers?.map((server) => (
<SelectItem
key={server.serverId}
value={server.serverId}
>
<span className="flex items-center gap-2 justify-between w-full">
<span>{server.name}</span>
<span className="text-muted-foreground text-xs self-center">
{server.ipAddress}
</span>
</span>
</span>
</SelectItem>
))}
<SelectLabel>Servers ({servers?.length})</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</SelectItem>
))}
<SelectLabel>Servers ({servers?.length})</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
)}
<FormField
control={form.control}
name="appName"

View File

@@ -1,3 +1,9 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { AlertTriangle, Database, HelpCircle } from "lucide-react";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import {
MariadbIcon,
MongodbIcon,
@@ -37,14 +43,14 @@ import {
} from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { slugify } from "@/lib/slug";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { AlertTriangle, Database } from "lucide-react";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
type DbType = typeof mySchema._type.type;
@@ -163,6 +169,8 @@ export const AddDatabase = ({ projectId, projectName }: Props) => {
const mariadbMutation = api.mariadb.create.useMutation();
const mysqlMutation = api.mysql.create.useMutation();
const hasServers = servers && servers.length > 0;
const form = useForm<AddDatabase>({
defaultValues: {
type: "postgres",
@@ -374,45 +382,62 @@ export const AddDatabase = ({ projectId, projectName }: Props) => {
</FormItem>
)}
/>
<FormField
control={form.control}
name="serverId"
render={({ field }) => (
<FormItem>
<FormLabel>Select a Server</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value || ""}
>
<SelectTrigger>
<SelectValue placeholder="Select a Server" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{servers?.map((server) => (
<SelectItem
key={server.serverId}
value={server.serverId}
>
{server.name}
</SelectItem>
))}
<SelectLabel>
Servers ({servers?.length})
</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
{hasServers && (
<FormField
control={form.control}
name="serverId"
render={({ field }) => (
<FormItem>
<FormLabel>Select a Server</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value || ""}
>
<SelectTrigger>
<SelectValue placeholder="Select a Server" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{servers?.map((server) => (
<SelectItem
key={server.serverId}
value={server.serverId}
>
{server.name}
</SelectItem>
))}
<SelectLabel>
Servers ({servers?.length})
</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
)}
<FormField
control={form.control}
name="appName"
render={({ field }) => (
<FormItem>
<FormLabel>App Name</FormLabel>
<FormLabel className="flex items-center gap-2">
App Name
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4 text-muted-foreground" />
</TooltipTrigger>
<TooltipContent side="right">
<p>
This will be the name of the Docker Swarm
service
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</FormLabel>
<FormControl>
<Input placeholder="my-app" {...field} />
</FormControl>

View File

@@ -1,3 +1,18 @@
import {
BookText,
CheckIcon,
ChevronsUpDown,
Globe,
HelpCircle,
LayoutGrid,
List,
Loader2,
PuzzleIcon,
SearchIcon,
} from "lucide-react";
import Link from "next/link";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import { GithubIcon } from "@/components/icons/data-tools-icons";
import { AlertBlock } from "@/components/shared/alert-block";
import {
@@ -54,21 +69,6 @@ import {
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { api } from "@/utils/api";
import {
BookText,
CheckIcon,
ChevronsUpDown,
Globe,
HelpCircle,
LayoutGrid,
List,
Loader2,
PuzzleIcon,
SearchIcon,
} from "lucide-react";
import Link from "next/link";
import { useEffect, useState } from "react";
import { toast } from "sonner";
const TEMPLATE_BASE_URL_KEY = "dokploy_template_base_url";
@@ -137,6 +137,8 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => {
return matchesTags && matchesQuery;
}) || [];
const hasServers = servers && servers.length > 0;
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger className="w-full">
@@ -425,60 +427,62 @@ export const AddTemplate = ({ projectId, baseUrl }: Props) => {
project.
</AlertDialogDescription>
<div>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<Label className="break-all w-fit flex flex-row gap-1 items-center pb-2 pt-3.5">
Select a Server{" "}
{!isCloud ? "(Optional)" : ""}
<HelpCircle className="size-4 text-muted-foreground" />
</Label>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
If no server is selected, the application
will be deployed on the server where the
user is logged in.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
{hasServers && (
<div>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<Label className="break-all w-fit flex flex-row gap-1 items-center pb-2 pt-3.5">
Select a Server{" "}
{!isCloud ? "(Optional)" : ""}
<HelpCircle className="size-4 text-muted-foreground" />
</Label>
</TooltipTrigger>
<TooltipContent
className="z-[999] w-[300px]"
align="start"
side="top"
>
<span>
If no server is selected, the
application will be deployed on the
server where the user is logged in.
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Select
onValueChange={(e) => {
setServerId(e);
}}
>
<SelectTrigger>
<SelectValue placeholder="Select a Server" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{servers?.map((server) => (
<SelectItem
key={server.serverId}
value={server.serverId}
>
<span className="flex items-center gap-2 justify-between w-full">
<span>{server.name}</span>
<span className="text-muted-foreground text-xs self-center">
{server.ipAddress}
<Select
onValueChange={(e) => {
setServerId(e);
}}
>
<SelectTrigger>
<SelectValue placeholder="Select a Server" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{servers?.map((server) => (
<SelectItem
key={server.serverId}
value={server.serverId}
>
<span className="flex items-center gap-2 justify-between w-full">
<span>{server.name}</span>
<span className="text-muted-foreground text-xs self-center">
{server.ipAddress}
</span>
</span>
</span>
</SelectItem>
))}
<SelectLabel>
Servers ({servers?.length})
</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
</div>
</SelectItem>
))}
<SelectLabel>
Servers ({servers?.length})
</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
</div>
)}
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>

View File

@@ -25,6 +25,7 @@ const examples = [
export const StepOne = ({ setTemplateInfo, templateInfo }: any) => {
// Get servers from the API
const { data: servers } = api.server.withSSHKey.useQuery();
const hasServers = servers && servers.length > 0;
const handleExampleClick = (example: string) => {
setTemplateInfo({ ...templateInfo, userInput: example });
@@ -47,37 +48,39 @@ export const StepOne = ({ setTemplateInfo, templateInfo }: any) => {
/>
</div>
<div className="space-y-2">
<Label htmlFor="server-deploy">
Select the server where you want to deploy (optional)
</Label>
<Select
value={templateInfo.server?.serverId}
onValueChange={(value) => {
const server = servers?.find((s) => s.serverId === value);
if (server) {
setTemplateInfo({
...templateInfo,
server: server,
});
}
}}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select a server" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{servers?.map((server) => (
<SelectItem key={server.serverId} value={server.serverId}>
{server.name}
</SelectItem>
))}
<SelectLabel>Servers ({servers?.length})</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
</div>
{hasServers && (
<div className="space-y-2">
<Label htmlFor="server-deploy">
Select the server where you want to deploy (optional)
</Label>
<Select
value={templateInfo.server?.serverId}
onValueChange={(value) => {
const server = servers?.find((s) => s.serverId === value);
if (server) {
setTemplateInfo({
...templateInfo,
server: server,
});
}
}}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="Select a server" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{servers?.map((server) => (
<SelectItem key={server.serverId} value={server.serverId}>
{server.name}
</SelectItem>
))}
<SelectLabel>Servers ({servers?.length})</SelectLabel>
</SelectGroup>
</SelectContent>
</Select>
</div>
)}
<div className="space-y-2">
<Label>Examples:</Label>

View File

@@ -199,7 +199,7 @@ export const StepTwo = ({ templateInfo, setTemplateInfo }: StepProps) => {
<p className="text-muted-foreground">
Generating template suggestions based on your input...
</p>
<pre>{templateInfo.userInput}</pre>
<pre className="whitespace-normal">{templateInfo.userInput}</pre>
</div>
);
}

View File

@@ -70,6 +70,7 @@ export const HandleDestinations = ({ destinationId }: Props) => {
},
{
enabled: !!destinationId,
refetchOnWindowFocus: false,
},
);
const {

View File

@@ -33,6 +33,7 @@ import { AddGithubProvider } from "./github/add-github-provider";
import { EditGithubProvider } from "./github/edit-github-provider";
import { AddGitlabProvider } from "./gitlab/add-gitlab-provider";
import { EditGitlabProvider } from "./gitlab/edit-gitlab-provider";
import { Badge } from "@/components/ui/badge";
export const ShowGitProviders = () => {
const { data, isLoading, refetch } = api.gitProvider.getAll.useQuery();
@@ -158,7 +159,13 @@ export const ShowGitProviders = () => {
<div className="flex flex-row gap-1">
{!haveGithubRequirements && isGithub && (
<div className="flex flex-col gap-1">
<div className="flex flex-row gap-1 items-center">
<Badge
variant="outline"
className="text-xs"
>
Action Required
</Badge>
<Link
href={`${gitProvider?.github?.githubAppName}/installations/new?state=gh_setup:${gitProvider?.github.githubId}`}
className={buttonVariants({
@@ -185,7 +192,13 @@ export const ShowGitProviders = () => {
</div>
)}
{!haveGitlabRequirements && isGitlab && (
<div className="flex flex-col gap-1">
<div className="flex flex-row gap-1 items-center">
<Badge
variant="outline"
className="text-xs"
>
Action Required
</Badge>
<Link
href={getGitlabUrl(
gitProvider.gitlab?.applicationId || "",

View File

@@ -36,6 +36,7 @@ const profileSchema = z.object({
password: z.string().nullable(),
currentPassword: z.string().nullable(),
image: z.string().optional(),
name: z.string().optional(),
allowImpersonation: z.boolean().optional().default(false),
});
@@ -84,6 +85,7 @@ export const ProfileForm = () => {
image: data?.user?.image || "",
currentPassword: "",
allowImpersonation: data?.user?.allowImpersonation || false,
name: data?.user?.name || "",
},
resolver: zodResolver(profileSchema),
});
@@ -97,6 +99,7 @@ export const ProfileForm = () => {
image: data?.user?.image || "",
currentPassword: form.getValues("currentPassword") || "",
allowImpersonation: data?.user?.allowImpersonation,
name: data?.user?.name || "",
},
{
keepValues: true,
@@ -119,6 +122,7 @@ export const ProfileForm = () => {
image: values.image,
currentPassword: values.currentPassword || undefined,
allowImpersonation: values.allowImpersonation,
name: values.name || undefined,
})
.then(async () => {
await refetch();
@@ -128,6 +132,7 @@ export const ProfileForm = () => {
password: "",
image: values.image,
currentPassword: "",
name: values.name || "",
});
})
.catch(() => {
@@ -167,6 +172,19 @@ export const ProfileForm = () => {
className="grid gap-4"
>
<div className="space-y-4">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input placeholder="Name" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"

View File

@@ -20,7 +20,9 @@ export const ToggleDockerCleanup = ({ serverId }: Props) => {
},
);
const enabled = data?.user.enableDockerCleanup || server?.enableDockerCleanup;
const enabled = serverId
? server?.enableDockerCleanup
: data?.user.enableDockerCleanup;
const { mutateAsync } = api.settings.updateDockerCleanup.useMutation();

View File

@@ -1,3 +1,11 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { PlusIcon } from "lucide-react";
import Link from "next/link";
import { useTranslation } from "next-i18next";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import {
@@ -30,14 +38,6 @@ import {
} from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { PlusIcon } from "lucide-react";
import { useTranslation } from "next-i18next";
import Link from "next/link";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const Schema = z.object({
name: z.string().min(1, {
@@ -218,7 +218,7 @@ export const HandleServers = ({ serverId }: Props) => {
</AlertBlock>
</div>
{!canCreateMoreServers && (
<AlertBlock type="warning">
<AlertBlock type="warning" className="mt-4">
You cannot create more servers,{" "}
<Link href="/dashboard/settings/billing" className="text-primary">
Please upgrade your plan

View File

@@ -1,3 +1,9 @@
import { format } from "date-fns";
import { KeyIcon, Loader2, MoreHorizontal, ServerIcon } from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/router";
import { useTranslation } from "next-i18next";
import { toast } from "sonner";
import { AlertBlock } from "@/components/shared/alert-block";
import { DialogAction } from "@/components/shared/dialog-action";
import { Badge } from "@/components/ui/badge";
@@ -27,12 +33,6 @@ import {
TableRow,
} from "@/components/ui/table";
import { api } from "@/utils/api";
import { format } from "date-fns";
import { KeyIcon, Loader2, MoreHorizontal, ServerIcon } from "lucide-react";
import { useTranslation } from "next-i18next";
import Link from "next/link";
import { useRouter } from "next/router";
import { toast } from "sonner";
import { ShowNodesModal } from "../cluster/nodes/show-nodes-modal";
import { TerminalModal } from "../web-server/terminal-modal";
import { ShowServerActions } from "./actions/show-server-actions";
@@ -115,24 +115,6 @@ export const ShowServers = () => {
</div>
) : (
<div className="flex flex-col gap-4 min-h-[25vh]">
{!canCreateMoreServers && (
<AlertBlock type="warning">
<div className="flex flex-row items-center gap-3 justify-center">
<span>
<div>
You cannot create more servers,{" "}
<Link
href="/dashboard/settings/billing"
className="text-primary"
>
Please upgrade your plan
</Link>
</div>
</span>
</div>
</AlertBlock>
)}
<Table>
<TableCaption>
<div className="flex flex-col gap-4">

View File

@@ -1,3 +1,9 @@
import { zodResolver } from "@hookform/resolvers/zod";
import Link from "next/link";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import { AlertBlock } from "@/components/shared/alert-block";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
@@ -22,12 +28,6 @@ import {
} from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import Link from "next/link";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const Schema = z.object({
name: z.string().min(1, {
@@ -108,7 +108,7 @@ export const CreateServer = ({ stepper }: Props) => {
<Card className="bg-background flex flex-col gap-4">
<div className="flex flex-col gap-2 pt-5 px-4">
{!canCreateMoreServers && (
<AlertBlock type="warning">
<AlertBlock type="warning" className="mt-2">
You cannot create more servers,{" "}
<Link href="/dashboard/settings/billing" className="text-primary">
Please upgrade your plan

View File

@@ -1,18 +1,22 @@
import copy from "copy-to-clipboard";
import { CopyIcon, ExternalLinkIcon, Loader2 } from "lucide-react";
import Link from "next/link";
import { useEffect, useRef, useState } from "react";
import { toast } from "sonner";
import { CodeEditor } from "@/components/shared/code-editor";
import { Card, CardContent } from "@/components/ui/card";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { api } from "@/utils/api";
import copy from "copy-to-clipboard";
import { ExternalLinkIcon, Loader2 } from "lucide-react";
import { CopyIcon } from "lucide-react";
import Link from "next/link";
import { useEffect, useRef } from "react";
import { toast } from "sonner";
export const CreateSSHKey = () => {
const { data, refetch } = api.sshKey.all.useQuery();
const generateMutation = api.sshKey.generate.useMutation();
const { mutateAsync, isLoading } = api.sshKey.create.useMutation();
const hasCreatedKey = useRef(false);
const [selectedOption, setSelectedOption] = useState<"manual" | "provider">(
"manual",
);
const cloudSSHKey = data?.find(
(sshKey) => sshKey.name === "dokploy-cloud-ssh-key",
@@ -60,89 +64,122 @@ export const CreateSSHKey = () => {
</div>
) : (
<>
<div className="flex flex-col gap-2 text-sm text-muted-foreground">
<div className="flex flex-col gap-4 text-sm text-muted-foreground">
<p className="text-primary text-base font-semibold">
You have two options to add SSH Keys to your server:
Choose how to add SSH Keys to your server:
</p>
<ul>
<li>1. Add The SSH Key to Server Manually</li>
{/* Radio button options */}
<div className="grid gap-2">
<RadioGroup
value={selectedOption}
onValueChange={(value) => {
setSelectedOption(value as "manual" | "provider");
}}
className="grid gap-3"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="manual" id="manual" />
<Label
htmlFor="manual"
className="text-primary font-medium cursor-pointer"
>
Add SSH Key to Server Manually
</Label>
</div>
<li>
2. Add the public SSH Key when you create a server in your
preffered provider (Hostinger, Digital Ocean, Hetzner, etc){" "}
</li>
</ul>
<div className="flex flex-col gap-2 w-full border rounded-lg p-4">
<span className="text-base font-semibold text-primary">
Option 1
</span>
<ul>
<li className="items-center flex gap-1">
1. Login to your server{" "}
</li>
<li>
2. When you are logged in run the following command
<div className="flex relative flex-col gap-4 w-full mt-2">
<CodeEditor
lineWrapping
language="properties"
value={`echo "${cloudSSHKey?.publicKey}" >> ~/.ssh/authorized_keys`}
readOnly
className="font-mono opacity-60"
/>
<button
type="button"
className="absolute right-2 top-2"
onClick={() => {
copy(
`echo "${cloudSSHKey?.publicKey}" >> ~/.ssh/authorized_keys`,
);
toast.success("Copied to clipboard");
}}
>
<CopyIcon className="size-4" />
</button>
</div>
</li>
<li className="mt-1">
3. You're done, follow the next step to insert the details
of your server.
</li>
</ul>
<div className="flex items-center space-x-2">
<RadioGroupItem value="provider" id="provider" />
<Label
htmlFor="provider"
className="text-primary font-medium cursor-pointer"
>
Add SSH Key when creating server in your provider
</Label>
</div>
</RadioGroup>
</div>
<div className="flex flex-col gap-2 w-full mt-2 border rounded-lg p-4">
<span className="text-base font-semibold text-primary">
Option 2
</span>
<div className="flex flex-col gap-4 w-full overflow-auto">
<div className="flex relative flex-col gap-2 overflow-y-auto">
<div className="text-sm text-primary flex flex-row gap-2 items-center">
Copy Public Key
<button
type="button"
className="right-2 top-8"
onClick={() => {
copy(
cloudSSHKey?.publicKey || "Generate a SSH Key",
);
toast.success("SSH Copied to clipboard");
}}
>
<CopyIcon className="size-4 text-muted-foreground" />
</button>
{/* Content based on selected option */}
{selectedOption === "manual" && (
<div className="flex flex-col gap-2 w-full border rounded-lg p-4">
<span className="text-base font-semibold text-primary">
Manual Setup Instructions
</span>
<ul className="space-y-2">
<li className="items-center flex gap-1">
1. Login to your server
</li>
<li>
2. When you are logged in run the following command
<div className="flex relative flex-col gap-4 w-full mt-2">
<CodeEditor
lineWrapping
language="properties"
value={`echo "${cloudSSHKey?.publicKey}" >> ~/.ssh/authorized_keys`}
readOnly
className="font-mono opacity-60"
/>
<button
type="button"
className="absolute right-2 top-2"
onClick={() => {
copy(
`echo "${cloudSSHKey?.publicKey}" >> ~/.ssh/authorized_keys`,
);
toast.success("Copied to clipboard");
}}
>
<CopyIcon className="size-4" />
</button>
</div>
</li>
<li className="mt-1">
3. You're done, follow the next step to insert the
details of your server.
</li>
</ul>
</div>
)}
{selectedOption === "provider" && (
<div className="flex flex-col gap-2 w-full border rounded-lg p-4">
<span className="text-base font-semibold text-primary">
Provider Setup Instructions
</span>
<div className="flex flex-col gap-4 w-full overflow-auto">
<div className="flex relative flex-col gap-2 overflow-y-auto">
<div className="text-sm text-primary flex flex-row gap-2 items-center">
Copy Public Key
<button
type="button"
className="right-2 top-8"
onClick={() => {
copy(
cloudSSHKey?.publicKey || "Generate a SSH Key",
);
toast.success("SSH Copied to clipboard");
}}
>
<CopyIcon className="size-4 text-muted-foreground" />
</button>
</div>
</div>
</div>
<p className="text-sm mt-2">
Use this public key when creating a server in your
preferred provider (Hostinger, Digital Ocean, Hetzner,
etc.)
</p>
<Link
href="https://docs.dokploy.com/docs/core/multi-server/instructions#requirements"
target="_blank"
className="text-primary flex flex-row gap-2 mt-2"
>
View Tutorial <ExternalLinkIcon className="size-4" />
</Link>
</div>
<Link
href="https://docs.dokploy.com/docs/core/multi-server/instructions#requirements"
target="_blank"
className="text-primary flex flex-row gap-2"
>
View Tutorial <ExternalLinkIcon className="size-4" />
</Link>
</div>
)}
</div>
</>
)}

View File

@@ -96,10 +96,7 @@ type SingleNavItem = {
title: string;
url: string;
icon?: LucideIcon;
isEnabled?: (opts: {
auth?: AuthQueryOutput;
isCloud: boolean;
}) => boolean;
isEnabled?: (opts: { auth?: AuthQueryOutput; isCloud: boolean }) => boolean;
};
// NavItem type
@@ -125,10 +122,7 @@ type ExternalLink = {
name: string;
url: string;
icon: React.ComponentType<{ className?: string }>;
isEnabled?: (opts: {
auth?: AuthQueryOutput;
isCloud: boolean;
}) => boolean;
isEnabled?: (opts: { auth?: AuthQueryOutput; isCloud: boolean }) => boolean;
};
// Menu type

View File

@@ -1,6 +1,6 @@
{
"name": "dokploy",
"version": "v0.24.3",
"version": "v0.24.5",
"private": true,
"license": "Apache-2.0",
"type": "module",
@@ -187,10 +187,10 @@
"ct3aMetadata": {
"initVersion": "7.25.2"
},
"packageManager": "pnpm@9.5.0",
"packageManager": "pnpm@9.12.0",
"engines": {
"node": "^20.16.0",
"pnpm": ">=9.5.0"
"pnpm": ">=9.12.0"
},
"lint-staged": {
"*": [
@@ -198,6 +198,8 @@
]
},
"commitlint": {
"extends": ["@commitlint/config-conventional"]
"extends": [
"@commitlint/config-conventional"
]
}
}

View File

@@ -5,14 +5,14 @@ import { myQueue } from "@/server/queues/queueSetup";
import { deploy } from "@/server/utils/deploy";
import {
IS_CLOUD,
checkUserRepositoryPermissions,
createPreviewDeployment,
createSecurityBlockedComment,
findGithubById,
findPreviewDeploymentByApplicationId,
findPreviewDeploymentsByPullRequestId,
removePreviewDeployment,
shouldDeploy,
findGithubById,
checkUserRepositoryPermissions,
createSecurityBlockedComment,
} from "@dokploy/server";
import { Webhooks } from "@octokit/webhooks";
import { and, eq } from "drizzle-orm";

View File

@@ -37,7 +37,6 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
import { validateRequest } from "@dokploy/server/lib/auth";

View File

@@ -33,7 +33,6 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
import { validateRequest } from "@dokploy/server/lib/auth";

View File

@@ -28,6 +28,8 @@ import { projectRouter } from "./routers/project";
import { redirectsRouter } from "./routers/redirects";
import { redisRouter } from "./routers/redis";
import { registryRouter } from "./routers/registry";
import { rollbackRouter } from "./routers/rollbacks";
import { scheduleRouter } from "./routers/schedule";
import { securityRouter } from "./routers/security";
import { serverRouter } from "./routers/server";
import { settingsRouter } from "./routers/settings";
@@ -35,8 +37,6 @@ import { sshRouter } from "./routers/ssh-key";
import { stripeRouter } from "./routers/stripe";
import { swarmRouter } from "./routers/swarm";
import { userRouter } from "./routers/user";
import { scheduleRouter } from "./routers/schedule";
import { rollbackRouter } from "./routers/rollbacks";
import { volumeBackupsRouter } from "./routers/volume-backups";
/**
* This is the primary router for your server.

View File

@@ -20,8 +20,8 @@ import {
} from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import { desc, eq } from "drizzle-orm";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "../trpc";
export const deploymentRouter = createTRPCRouter({
all: protectedProcedure

View File

@@ -8,9 +8,9 @@ import {
getServiceContainersByAppName,
getStackContainersByAppName,
} from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { TRPCError } from "@trpc/server";
export const containerIdRegex = /^[a-zA-Z0-9.\-_]+$/;

View File

@@ -12,8 +12,8 @@ import {
getServiceContainer,
updateMount,
} from "@dokploy/server";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "../trpc";
export const mountRouter = createTRPCRouter({
create: protectedProcedure

View File

@@ -361,6 +361,7 @@ export const projectRouter = createTRPCRouter({
previewDeployments,
mounts,
appName,
refreshToken,
...application
} = await findApplicationById(id);
const newAppName = appName.substring(
@@ -603,8 +604,14 @@ export const projectRouter = createTRPCRouter({
break;
}
case "compose": {
const { composeId, mounts, domains, appName, ...compose } =
await findComposeById(id);
const {
composeId,
mounts,
domains,
appName,
refreshToken,
...compose
} = await findComposeById(id);
const newAppName = appName.substring(
0,

View File

@@ -4,10 +4,10 @@ import {
getNodeInfo,
getSwarmNodes,
} from "@dokploy/server";
import { findServerById } from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { TRPCError } from "@trpc/server";
import { findServerById } from "@dokploy/server";
import { containerIdRegex } from "./docker";
export const swarmRouter = createTRPCRouter({

View File

@@ -1,30 +1,30 @@
import { removeJob, schedule, updateJob } from "@/server/utils/backup";
import {
IS_CLOUD,
updateVolumeBackup,
removeVolumeBackup,
createVolumeBackup,
runVolumeBackup,
findVolumeBackupById,
restoreVolume,
scheduleVolumeBackup,
removeVolumeBackup,
removeVolumeBackupJob,
restoreVolume,
runVolumeBackup,
scheduleVolumeBackup,
updateVolumeBackup,
} from "@dokploy/server";
import { db } from "@dokploy/server/db";
import {
createVolumeBackupSchema,
updateVolumeBackupSchema,
volumeBackups,
} from "@dokploy/server/db/schema";
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { db } from "@dokploy/server/db";
import { eq } from "drizzle-orm";
import { observable } from "@trpc/server/observable";
import {
execAsyncRemote,
execAsyncStream,
} from "@dokploy/server/utils/process/execAsync";
import { removeJob, schedule, updateJob } from "@/server/utils/backup";
import { TRPCError } from "@trpc/server";
import { observable } from "@trpc/server/observable";
import { eq } from "drizzle-orm";
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "../trpc";
export const volumeBackupsRouter = createTRPCRouter({
list: protectedProcedure

View File

@@ -5,6 +5,7 @@ import {
initializeTraefik,
} from "@dokploy/server/setup/traefik-setup";
import { execAsync } from "@dokploy/server";
import { setupDirectories } from "@dokploy/server/setup/config-paths";
import { initializePostgres } from "@dokploy/server/setup/postgres-setup";
import { initializeRedis } from "@dokploy/server/setup/redis-setup";
@@ -12,7 +13,6 @@ import {
initializeNetwork,
initializeSwarm,
} from "@dokploy/server/setup/setup";
import { execAsync } from "@dokploy/server";
(async () => {
try {
setupDirectories();

View File

@@ -13,10 +13,7 @@ declare global {
baseDomain?: string;
};
chatwootSDK?: {
run: (config: {
websiteToken: string;
baseUrl: string;
}) => void;
run: (config: { websiteToken: string; baseUrl: string }) => void;
};
$chatwoot?: {
setUser: (

View File

@@ -29,5 +29,9 @@
"tsx": "^4.16.2",
"typescript": "^5.8.3"
},
"packageManager": "pnpm@9.5.0"
"packageManager": "pnpm@9.12.0",
"engines": {
"node": "^20.16.0",
"pnpm": ">=9.12.0"
}
}

View File

@@ -1,18 +1,17 @@
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"files": {
"ignore": [
"node_modules/**",
".next/**",
"drizzle/**",
".docker",
"dist",
"packages/server/package.json"
"includes": [
"**",
"!**/.docker",
"!**/.next/**",
"!**/dist",
"!**/drizzle/**",
"!node_modules/**",
"!packages/server/package.json"
]
},
"organizeImports": {
"enabled": true
},
"assist": { "actions": { "source": { "organizeImports": "on" } } },
"linter": {
"rules": {
"security": {
@@ -20,7 +19,8 @@
},
"complexity": {
"noUselessCatch": "off",
"noBannedTypes": "off"
"noBannedTypes": "off",
"noUselessFragments": "off"
},
"correctness": {
"useExhaustiveDependencies": "off",
@@ -30,7 +30,17 @@
"noUnusedVariables": "error"
},
"style": {
"noNonNullAssertion": "off"
"noNonNullAssertion": "off",
"noParameterAssign": "error",
"useAsConstAssertion": "error",
"useDefaultParameterLast": "error",
"useEnumInitializers": "error",
"useSelfClosingElements": "error",
"useSingleVarDeclarator": "error",
"noUnusedTemplateLiteral": "error",
"useNumberNamespace": "error",
"noInferrableTypes": "error",
"noUselessElse": "error"
},
"suspicious": {
"noArrayIndexKey": "off",

View File

@@ -1,7 +1,10 @@
{
"name": "dokploy",
"private": true,
"workspaces": ["apps/*", "packages/*"],
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"dokploy:setup": "pnpm --filter=dokploy run setup",
"dokploy:dev": "pnpm --filter=dokploy run dev",
@@ -20,7 +23,7 @@
"format-and-lint:fix": "biome check . --write"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@biomejs/biome": "2.1.1",
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@types/node": "^18.19.104",
@@ -30,10 +33,10 @@
"lint-staged": "^15.5.2",
"tsx": "4.16.2"
},
"packageManager": "pnpm@9.5.0",
"packageManager": "pnpm@9.12.0",
"engines": {
"node": "^20.16.0",
"pnpm": ">=9.5.0"
"pnpm": ">=9.12.0"
},
"lint-staged": {
"*": [
@@ -41,7 +44,9 @@
]
},
"commitlint": {
"extends": ["@commitlint/config-conventional"]
"extends": [
"@commitlint/config-conventional"
]
},
"resolutions": {
"@types/react": "18.3.5",

View File

@@ -105,5 +105,10 @@
"tsc-alias": "1.8.10",
"tsx": "^4.16.2",
"typescript": "^5.8.3"
},
"packageManager": "pnpm@9.12.0",
"engines": {
"node": "^20.16.0",
"pnpm": ">=9.12.0"
}
}
}

View File

@@ -13,9 +13,9 @@ import { applications } from "./application";
import { backups } from "./backups";
import { compose } from "./compose";
import { previewDeployments } from "./preview-deployments";
import { rollbacks } from "./rollbacks";
import { schedules } from "./schedule";
import { server } from "./server";
import { rollbacks } from "./rollbacks";
import { volumeBackups } from "./volume-backups";
export const deploymentStatus = pgEnum("deploymentStatus", [
"running",

View File

@@ -1,14 +1,14 @@
import type { Application } from "@dokploy/server/services/application";
import type { Mount } from "@dokploy/server/services/mount";
import type { Port } from "@dokploy/server/services/port";
import type { Project } from "@dokploy/server/services/project";
import type { Registry } from "@dokploy/server/services/registry";
import { relations } from "drizzle-orm";
import { jsonb, pgTable, serial, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { deployments } from "./deployment";
import type { Application } from "@dokploy/server/services/application";
import type { Project } from "@dokploy/server/services/project";
import type { Mount } from "@dokploy/server/services/mount";
import type { Port } from "@dokploy/server/services/port";
import type { Registry } from "@dokploy/server/services/registry";
export const rollbacks = pgTable("rollback", {
rollbackId: text("rollbackId")

View File

@@ -1,3 +1,4 @@
import { paths } from "@dokploy/server/constants";
import { relations } from "drizzle-orm";
import {
boolean,
@@ -15,7 +16,6 @@ import { backups } from "./backups";
import { projects } from "./project";
import { schedules } from "./schedule";
import { certificateType } from "./shared";
import { paths } from "@dokploy/server/constants";
/**
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
* database instance for multiple projects.
@@ -323,6 +323,7 @@ export const apiUpdateWebServerMonitoring = z.object({
export const apiUpdateUser = createSchema.partial().extend({
password: z.string().optional(),
currentPassword: z.string().optional(),
name: z.string().optional(),
metricsConfig: z
.object({
server: z.object({

View File

@@ -3,16 +3,16 @@ import { boolean, integer, pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { serviceType } from "./mount";
import { applications } from "./application";
import { mongo } from "./mongo";
import { mysql } from "./mysql";
import { redis } from "./redis";
import { compose } from "./compose";
import { postgres } from "./postgres";
import { mariadb } from "./mariadb";
import { destinations } from "./destination";
import { deployments } from "./deployment";
import { destinations } from "./destination";
import { mariadb } from "./mariadb";
import { mongo } from "./mongo";
import { serviceType } from "./mount";
import { mysql } from "./mysql";
import { postgres } from "./postgres";
import { redis } from "./redis";
import { generateAppName } from "./utils";
export const volumeBackups = pgTable("volume_backup", {

View File

@@ -298,11 +298,7 @@ export const validateRequest = async (request: IncomingMessage) => {
const mockSession = {
session: {
user: {
id: apiKeyRecord.user.id,
email: apiKeyRecord.user.email,
name: apiKeyRecord.user.name,
},
userId: apiKeyRecord.user.id,
activeOrganizationId: organizationId || "",
},
user: {

View File

@@ -237,6 +237,7 @@ export const deployApplication = async ({
} catch (error) {
await updateDeploymentStatus(deployment.deploymentId, "error");
await updateApplicationStatus(applicationId, "error");
await sendBuildErrorNotifications({
projectName: application.project.name,
applicationName: application.name,
@@ -370,8 +371,9 @@ export const deployRemoteApplication = async ({
domains: application.domains,
});
} catch (error) {
// @ts-ignore
const encodedContent = encodeBase64(error?.message);
const errorMessage = error instanceof Error ? error.message : String(error);
const encodedContent = encodeBase64(errorMessage);
await execAsyncRemote(
application.serverId,
@@ -383,12 +385,12 @@ export const deployRemoteApplication = async ({
await updateDeploymentStatus(deployment.deploymentId, "error");
await updateApplicationStatus(applicationId, "error");
await sendBuildErrorNotifications({
projectName: application.project.name,
applicationName: application.name,
applicationType: "application",
// @ts-ignore
errorMessage: error?.message || "Error building",
errorMessage: `Please check the logs for details: ${errorMessage}`,
buildLink,
organizationId: application.project.organizationId,
});

View File

@@ -31,8 +31,8 @@ import {
findPreviewDeploymentById,
updatePreviewDeployment,
} from "./preview-deployment";
import { findScheduleById } from "./schedule";
import { removeRollbackById } from "./rollbacks";
import { findScheduleById } from "./schedule";
import { findVolumeBackupById } from "./volume-backups";
export type Deployment = typeof deployments.$inferSelect;

View File

@@ -1,27 +1,27 @@
import type { CreateServiceOptions } from "dockerode";
import { eq } from "drizzle-orm";
import type { z } from "zod";
import { db } from "../db";
import {
type createRollbackSchema,
rollbacks,
deployments as deploymentsSchema,
rollbacks,
} from "../db/schema";
import type { z } from "zod";
import { type Application, findApplicationById } from "./application";
import { getRemoteDocker } from "../utils/servers/remote-docker";
import { getAuthConfig, type ApplicationNested } from "../utils/builders";
import { execAsync, execAsyncRemote } from "../utils/process/execAsync";
import type { CreateServiceOptions } from "dockerode";
import { findDeploymentById } from "./deployment";
import { type ApplicationNested, getAuthConfig } from "../utils/builders";
import {
prepareEnvironmentVariables,
calculateResources,
generateBindMounts,
generateConfigContainer,
generateVolumeMounts,
prepareEnvironmentVariables,
} from "../utils/docker/utils";
import type { Project } from "./project";
import { execAsync, execAsyncRemote } from "../utils/process/execAsync";
import { getRemoteDocker } from "../utils/servers/remote-docker";
import { type Application, findApplicationById } from "./application";
import { findDeploymentById } from "./deployment";
import type { Mount } from "./mount";
import type { Port } from "./port";
import type { Project } from "./project";
export const createRollback = async (
input: z.infer<typeof createRollbackSchema>,

View File

@@ -1,12 +1,12 @@
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import type { z } from "zod";
import { db } from "../db";
import {
type createVolumeBackupSchema,
type updateVolumeBackupSchema,
volumeBackups,
} from "../db/schema";
import { db } from "../db";
import { TRPCError } from "@trpc/server";
import type { z } from "zod";
export const findVolumeBackupById = async (volumeBackupId: string) => {
const volumeBackup = await db.query.volumeBackups.findFirst({

View File

@@ -191,6 +191,9 @@ export const createDefaultServerTraefikConfig = () => {
export const getDefaultTraefikConfig = () => {
const configObject: MainTraefikConfig = {
global: {
sendAnonymousUsage: false,
},
providers: {
...(process.env.NODE_ENV === "development"
? {

View File

@@ -1,8 +1,8 @@
import { findComposeById } from "@dokploy/server/services/compose";
import { dump, load } from "js-yaml";
import { addAppNameToAllServiceNames } from "./collision/root-network";
import { addSuffixToAllVolumes } from "./compose/volume";
import { generateRandomHash } from "./compose";
import { addSuffixToAllVolumes } from "./compose/volume";
import type { ComposeSpecification } from "./types";
export const addAppNameToPreventCollision = (

View File

@@ -65,6 +65,8 @@ export const sendBuildErrorNotifications = async ({
const decorate = (decoration: string, text: string) =>
`${discord.decoration ? decoration : ""} ${text}`.trim();
const limitCharacter = 800;
const truncatedErrorMessage = errorMessage.substring(0, limitCharacter);
await sendDiscordNotification(discord, {
title: decorate(">", "`⚠️` Build Failed"),
color: 0xed4245,
@@ -101,7 +103,7 @@ export const sendBuildErrorNotifications = async ({
},
{
name: decorate("`⚠️`", "Error Message"),
value: `\`\`\`${errorMessage}\`\`\``,
value: `\`\`\`${truncatedErrorMessage}\`\`\``,
},
{
name: decorate("`🧷`", "Build Link"),

View File

@@ -1,6 +1,6 @@
import fs, { writeFileSync } from "node:fs";
import path from "node:path";
import { createReadStream } from "node:fs";
import path from "node:path";
import { createInterface } from "node:readline";
import { paths } from "@dokploy/server/constants";
import type { Domain } from "@dokploy/server/services/domain";
@@ -237,7 +237,6 @@ export const writeTraefikConfigInPath = async (
} else {
fs.writeFileSync(configPath, traefikConfig, "utf8");
}
fs.writeFileSync(configPath, traefikConfig, "utf8");
} catch (e) {
console.error("Error saving the YAML config file:", e);
}

View File

@@ -1,12 +1,12 @@
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { paths } from "@dokploy/server/constants";
import type { Domain } from "@dokploy/server/services/domain";
import { dump, load } from "js-yaml";
import type { ApplicationNested } from "../builders";
import { execAsyncRemote } from "../process/execAsync";
import { writeTraefikConfigRemote } from "./application";
import type { FileConfig } from "./file-types";
import type { Domain } from "@dokploy/server/services/domain";
export const addMiddleware = (config: FileConfig, middlewareName: string) => {
if (config.http?.routers) {

View File

@@ -1,9 +1,9 @@
import type { findVolumeBackupById } from "@dokploy/server/services/volume-backups";
import { normalizeS3Path } from "../backups/utils";
import { getS3Credentials } from "../backups/utils";
import path from "node:path";
import { paths } from "@dokploy/server/constants";
import { findComposeById } from "@dokploy/server/services/compose";
import type { findVolumeBackupById } from "@dokploy/server/services/volume-backups";
import { normalizeS3Path } from "../backups/utils";
import { getS3Credentials } from "../backups/utils";
export const backupVolume = async (
volumeBackup: Awaited<ReturnType<typeof findVolumeBackupById>>,

View File

@@ -1,3 +1,4 @@
import path from "node:path";
import {
findApplicationById,
findComposeById,
@@ -5,7 +6,6 @@ import {
getS3Credentials,
paths,
} from "../..";
import path from "node:path";
export const restoreVolume = async (
id: string,

View File

@@ -1,4 +1,5 @@
import { findVolumeBackupById } from "@dokploy/server/services/volume-backups";
import { scheduleJob, scheduledJobs } from "node-schedule";
import {
createDeploymentVolumeBackup,
execAsync,
@@ -6,7 +7,6 @@ import {
updateDeploymentStatus,
} from "../..";
import { backupVolume } from "./backup";
import { scheduleJob, scheduledJobs } from "node-schedule";
export const scheduleVolumeBackup = async (volumeBackupId: string) => {
const volumeBackup = await findVolumeBackupById(volumeBackupId);

74
pnpm-lock.yaml generated
View File

@@ -13,8 +13,8 @@ importers:
.:
devDependencies:
'@biomejs/biome':
specifier: 1.9.4
version: 1.9.4
specifier: 2.1.1
version: 2.1.1
'@commitlint/cli':
specifier: ^19.8.1
version: 19.8.1(@types/node@18.19.104)(typescript@5.8.3)
@@ -933,55 +933,55 @@ packages:
'@better-fetch/fetch@1.1.18':
resolution: {integrity: sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==}
'@biomejs/biome@1.9.4':
resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==}
'@biomejs/biome@2.1.1':
resolution: {integrity: sha512-HFGYkxG714KzG+8tvtXCJ1t1qXQMzgWzfvQaUjxN6UeKv+KvMEuliInnbZLJm6DXFXwqVi6446EGI0sGBLIYng==}
engines: {node: '>=14.21.3'}
hasBin: true
'@biomejs/cli-darwin-arm64@1.9.4':
resolution: {integrity: sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==}
'@biomejs/cli-darwin-arm64@2.1.1':
resolution: {integrity: sha512-2Muinu5ok4tWxq4nu5l19el48cwCY/vzvI7Vjbkf3CYIQkjxZLyj0Ad37Jv2OtlXYaLvv+Sfu1hFeXt/JwRRXQ==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [darwin]
'@biomejs/cli-darwin-x64@1.9.4':
resolution: {integrity: sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==}
'@biomejs/cli-darwin-x64@2.1.1':
resolution: {integrity: sha512-cC8HM5lrgKQXLAK+6Iz2FrYW5A62pAAX6KAnRlEyLb+Q3+Kr6ur/sSuoIacqlp1yvmjHJqjYfZjPvHWnqxoEIA==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [darwin]
'@biomejs/cli-linux-arm64-musl@1.9.4':
resolution: {integrity: sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==}
'@biomejs/cli-linux-arm64-musl@2.1.1':
resolution: {integrity: sha512-/7FBLnTswu4jgV9ttI3AMIdDGqVEPIZd8I5u2D4tfCoj8rl9dnjrEQbAIDlWhUXdyWlFSz8JypH3swU9h9P+2A==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
'@biomejs/cli-linux-arm64@1.9.4':
resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==}
'@biomejs/cli-linux-arm64@2.1.1':
resolution: {integrity: sha512-tw4BEbhAUkWPe4WBr6IX04DJo+2jz5qpPzpW/SWvqMjb9QuHY8+J0M23V8EPY/zWU4IG8Ui0XESapR1CB49Q7g==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
'@biomejs/cli-linux-x64-musl@1.9.4':
resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==}
'@biomejs/cli-linux-x64-musl@2.1.1':
resolution: {integrity: sha512-kUu+loNI3OCD2c12cUt7M5yaaSjDnGIksZwKnueubX6c/HWUyi/0mPbTBHR49Me3F0KKjWiKM+ZOjsmC+lUt9g==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
'@biomejs/cli-linux-x64@1.9.4':
resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==}
'@biomejs/cli-linux-x64@2.1.1':
resolution: {integrity: sha512-3WJ1GKjU7NzZb6RTbwLB59v9cTIlzjbiFLDB0z4376TkDqoNYilJaC37IomCr/aXwuU8QKkrYoHrgpSq5ffJ4Q==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
'@biomejs/cli-win32-arm64@1.9.4':
resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==}
'@biomejs/cli-win32-arm64@2.1.1':
resolution: {integrity: sha512-vEHK0v0oW+E6RUWLoxb2isI3rZo57OX9ZNyyGH701fZPj6Il0Rn1f5DMNyCmyflMwTnIQstEbs7n2BxYSqQx4Q==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [win32]
'@biomejs/cli-win32-x64@1.9.4':
resolution: {integrity: sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==}
'@biomejs/cli-win32-x64@2.1.1':
resolution: {integrity: sha512-i2PKdn70kY++KEF/zkQFvQfX1e8SkA8hq4BgC+yE9dZqyLzB/XStY2MvwI3qswlRgnGpgncgqe0QYKVS1blksg==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [win32]
@@ -7281,39 +7281,39 @@ snapshots:
'@better-fetch/fetch@1.1.18': {}
'@biomejs/biome@1.9.4':
'@biomejs/biome@2.1.1':
optionalDependencies:
'@biomejs/cli-darwin-arm64': 1.9.4
'@biomejs/cli-darwin-x64': 1.9.4
'@biomejs/cli-linux-arm64': 1.9.4
'@biomejs/cli-linux-arm64-musl': 1.9.4
'@biomejs/cli-linux-x64': 1.9.4
'@biomejs/cli-linux-x64-musl': 1.9.4
'@biomejs/cli-win32-arm64': 1.9.4
'@biomejs/cli-win32-x64': 1.9.4
'@biomejs/cli-darwin-arm64': 2.1.1
'@biomejs/cli-darwin-x64': 2.1.1
'@biomejs/cli-linux-arm64': 2.1.1
'@biomejs/cli-linux-arm64-musl': 2.1.1
'@biomejs/cli-linux-x64': 2.1.1
'@biomejs/cli-linux-x64-musl': 2.1.1
'@biomejs/cli-win32-arm64': 2.1.1
'@biomejs/cli-win32-x64': 2.1.1
'@biomejs/cli-darwin-arm64@1.9.4':
'@biomejs/cli-darwin-arm64@2.1.1':
optional: true
'@biomejs/cli-darwin-x64@1.9.4':
'@biomejs/cli-darwin-x64@2.1.1':
optional: true
'@biomejs/cli-linux-arm64-musl@1.9.4':
'@biomejs/cli-linux-arm64-musl@2.1.1':
optional: true
'@biomejs/cli-linux-arm64@1.9.4':
'@biomejs/cli-linux-arm64@2.1.1':
optional: true
'@biomejs/cli-linux-x64-musl@1.9.4':
'@biomejs/cli-linux-x64-musl@2.1.1':
optional: true
'@biomejs/cli-linux-x64@1.9.4':
'@biomejs/cli-linux-x64@2.1.1':
optional: true
'@biomejs/cli-win32-arm64@1.9.4':
'@biomejs/cli-win32-arm64@2.1.1':
optional: true
'@biomejs/cli-win32-x64@1.9.4':
'@biomejs/cli-win32-x64@2.1.1':
optional: true
'@codemirror/autocomplete@6.18.6':

View File

@@ -1,6 +1,5 @@
packages:
- "apps/api"
- "apps/dokploy"
- "apps/monitoring"
- "apps/schedules"
- "packages/server"