chore: Reorder and clean up imports, update openapi schema, and improve

cache invalidation logic
This commit is contained in:
Mohammed Imran
2026-02-16 14:01:00 +05:30
parent ebbbd39065
commit 938b0b4ed3
19 changed files with 2323 additions and 214 deletions
+1
View File
@@ -12,6 +12,7 @@ vi.mock("@dokploy/server/db", () => {
chain.where = () => chain; chain.where = () => chain;
chain.values = () => chain; chain.values = () => chain;
chain.returning = () => Promise.resolve([{}]); chain.returning = () => Promise.resolve([{}]);
// biome-ignore lint/suspicious/noThenProperty: this is for testing
chain.then = undefined; chain.then = undefined;
const tableMock = { const tableMock = {
@@ -7,6 +7,7 @@ import { z } from "zod";
import { AlertBlock } from "@/components/shared/alert-block"; import { AlertBlock } from "@/components/shared/alert-block";
import { CodeEditor } from "@/components/shared/code-editor"; import { CodeEditor } from "@/components/shared/code-editor";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -24,7 +25,6 @@ import {
FormLabel, FormLabel,
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
@@ -1,8 +1,8 @@
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
import { badgeStateColor } from "@/components/dashboard/application/logs/show";
import { Badge } from "@/components/ui/badge";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { badgeStateColor } from "@/components/dashboard/application/logs/show";
import { Badge } from "@/components/ui/badge";
import { import {
Card, Card,
CardContent, CardContent,
@@ -79,7 +79,7 @@ export const AddCompose = ({ environmentId, projectName }: Props) => {
api.compose.create.useMutation(); api.compose.create.useMutation();
// Get environment data to extract projectId // Get environment data to extract projectId
const { data: environment } = api.environment.one.useQuery({ environmentId }); // const { data: environment } = api.environment.one.useQuery({ environmentId });
const hasServers = servers && servers.length > 0; const hasServers = servers && servers.length > 0;
// Show dropdown logic based on cloud environment // Show dropdown logic based on cloud environment
@@ -117,6 +117,8 @@ export const AddCompose = ({ environmentId, projectName }: Props) => {
await utils.environment.one.invalidate({ await utils.environment.one.invalidate({
environmentId, environmentId,
}); });
// Invalidate the project query to refresh the project data for the advance-breadcrumb
await utils.project.all.invalidate();
}) })
.catch(() => { .catch(() => {
toast.error("Error creating the compose"); toast.error("Error creating the compose");
@@ -98,6 +98,8 @@ export const AdvancedEnvironmentSelector = ({
toast.success("Environment created successfully"); toast.success("Environment created successfully");
utils.environment.byProjectId.invalidate({ projectId }); utils.environment.byProjectId.invalidate({ projectId });
// Invalidate the project query to refresh the project data for the advance-breadcrumb
utils.project.all.invalidate();
setIsCreateDialogOpen(false); setIsCreateDialogOpen(false);
setName(""); setName("");
setDescription(""); setDescription("");
@@ -19,9 +19,9 @@ import {
import { import {
Form, Form,
FormControl, FormControl,
FormDescription,
FormField, FormField,
FormItem, FormItem,
FormDescription,
FormLabel, FormLabel,
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
@@ -17,9 +17,9 @@ import {
import { import {
Form, Form,
FormControl, FormControl,
FormDescription,
FormField, FormField,
FormItem, FormItem,
FormDescription,
FormLabel, FormLabel,
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
@@ -19,9 +19,9 @@ import {
import { import {
Form, Form,
FormControl, FormControl,
FormDescription,
FormField, FormField,
FormItem, FormItem,
FormDescription,
FormLabel, FormLabel,
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
@@ -18,9 +18,9 @@ import {
import { import {
Form, Form,
FormControl, FormControl,
FormDescription,
FormField, FormField,
FormItem, FormItem,
FormDescription,
FormLabel, FormLabel,
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
@@ -1,7 +1,6 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { useHealthCheckAfterMutation } from "@/hooks/use-health-check-after-mutation";
import { toast } from "sonner"; import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
import { AlertBlock } from "@/components/shared/alert-block"; import { AlertBlock } from "@/components/shared/alert-block";
@@ -24,6 +23,7 @@ import {
FormLabel, FormLabel,
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
import { useHealthCheckAfterMutation } from "@/hooks/use-health-check-after-mutation";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
const schema = z.object({ const schema = z.object({
@@ -1,7 +1,6 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { ArrowRightLeft, Plus, Trash2 } from "lucide-react"; import { ArrowRightLeft, Plus, Trash2 } from "lucide-react";
import { useTranslation } from "next-i18next"; import { useTranslation } from "next-i18next";
import { useHealthCheckAfterMutation } from "@/hooks/use-health-check-after-mutation";
import type React from "react"; import type React from "react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useFieldArray, useForm } from "react-hook-form"; import { useFieldArray, useForm } from "react-hook-form";
@@ -36,6 +35,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { useHealthCheckAfterMutation } from "@/hooks/use-health-check-after-mutation";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
interface Props { interface Props {
@@ -2,8 +2,8 @@
import { useState } from "react"; import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { authClient } from "@/lib/auth-client";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { authClient } from "@/lib/auth-client";
export function SignInWithGithub() { export function SignInWithGithub() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -2,8 +2,8 @@
import { useState } from "react"; import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { authClient } from "@/lib/auth-client";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { authClient } from "@/lib/auth-client";
export function SignInWithGoogle() { export function SignInWithGoogle() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
+2 -2
View File
@@ -22,12 +22,12 @@ import { mountRouter } from "./routers/mount";
import { mysqlRouter } from "./routers/mysql"; import { mysqlRouter } from "./routers/mysql";
import { notificationRouter } from "./routers/notification"; import { notificationRouter } from "./routers/notification";
import { organizationRouter } from "./routers/organization"; import { organizationRouter } from "./routers/organization";
import { licenseKeyRouter } from "./routers/proprietary/license-key";
import { ssoRouter } from "./routers/proprietary/sso";
import { portRouter } from "./routers/port"; import { portRouter } from "./routers/port";
import { postgresRouter } from "./routers/postgres"; import { postgresRouter } from "./routers/postgres";
import { previewDeploymentRouter } from "./routers/preview-deployment"; import { previewDeploymentRouter } from "./routers/preview-deployment";
import { projectRouter } from "./routers/project"; import { projectRouter } from "./routers/project";
import { licenseKeyRouter } from "./routers/proprietary/license-key";
import { ssoRouter } from "./routers/proprietary/sso";
import { redirectsRouter } from "./routers/redirects"; import { redirectsRouter } from "./routers/redirects";
import { redisRouter } from "./routers/redis"; import { redisRouter } from "./routers/redis";
import { registryRouter } from "./routers/registry"; import { registryRouter } from "./routers/registry";
+2 -1
View File
@@ -1,8 +1,9 @@
import { exit } from "node:process";
import { exec } from "node:child_process"; import { exec } from "node:child_process";
import { exit } from "node:process";
import { promisify } from "node:util"; import { promisify } from "node:util";
const execAsync = promisify(exec); const execAsync = promisify(exec);
import { setupDirectories } from "@dokploy/server/setup/config-paths"; import { setupDirectories } from "@dokploy/server/setup/config-paths";
import { initializePostgres } from "@dokploy/server/setup/postgres-setup"; import { initializePostgres } from "@dokploy/server/setup/postgres-setup";
import { initializeRedis } from "@dokploy/server/setup/redis-setup"; import { initializeRedis } from "@dokploy/server/setup/redis-setup";
+2287 -184
View File
File diff suppressed because it is too large Load Diff
+13 -13
View File
@@ -105,21 +105,21 @@ export const suggestVariants = async ({
}), }),
prompt: ` prompt: `
Act as advanced DevOps engineer and analyze the user's request to determine the appropriate suggestions (up to 3 items). Act as advanced DevOps engineer and analyze the user's request to determine the appropriate suggestions (up to 3 items).
CRITICAL - Read the user's request carefully and follow the appropriate strategy: CRITICAL - Read the user's request carefully and follow the appropriate strategy:
Strategy A - If the user specifies a PARTICULAR APPLICATION/SERVICE (e.g., "deploy Chatwoot", "install sendingtk/chatwoot:develop", "setup Bitwarden"): Strategy A - If the user specifies a PARTICULAR APPLICATION/SERVICE (e.g., "deploy Chatwoot", "install sendingtk/chatwoot:develop", "setup Bitwarden"):
- Generate different deployment VARIANTS of that SAME application - Generate different deployment VARIANTS of that SAME application
- Each variant should be a different configuration (minimal, full stack, with different databases, development vs production, etc.) - Each variant should be a different configuration (minimal, full stack, with different databases, development vs production, etc.)
- Example: For "Chatwoot" → "Chatwoot with PostgreSQL", "Chatwoot Development", "Chatwoot Full Stack" - Example: For "Chatwoot" → "Chatwoot with PostgreSQL", "Chatwoot Development", "Chatwoot Full Stack"
- The name MUST include the specific application name the user mentioned - The name MUST include the specific application name the user mentioned
Strategy B - If the user describes a GENERAL NEED or USE CASE (e.g., "personal blog", "project management tool", "chat application"): Strategy B - If the user describes a GENERAL NEED or USE CASE (e.g., "personal blog", "project management tool", "chat application"):
- Suggest different open source projects that fulfill that need - Suggest different open source projects that fulfill that need
- Each suggestion should be a different tool/platform that solves the same problem - Each suggestion should be a different tool/platform that solves the same problem
- Example: For "personal blog" → "WordPress", "Ghost", "Hugo with Nginx" - Example: For "personal blog" → "WordPress", "Ghost", "Hugo with Nginx"
- The name should be the actual project name - The name should be the actual project name
Return your response as a JSON object with the following structure: Return your response as a JSON object with the following structure:
{ {
"suggestions": [ "suggestions": [
@@ -131,7 +131,7 @@ export const suggestVariants = async ({
} }
] ]
} }
Important rules for the response: Important rules for the response:
1. Use slug format for the id field (lowercase, hyphenated) 1. Use slug format for the id field (lowercase, hyphenated)
2. Determine which strategy to use based on whether the user specified a particular application or described a general need 2. Determine which strategy to use based on whether the user specified a particular application or described a general need
@@ -142,9 +142,9 @@ export const suggestVariants = async ({
7. The shortDescription should be a single-line summary focusing on key technologies or differentiators 7. The shortDescription should be a single-line summary focusing on key technologies or differentiators
8. All suggestions should be installable in docker and have docker compose support 8. All suggestions should be installable in docker and have docker compose support
9. Provide variety in your suggestions - different complexity levels, tech stacks, or approaches 9. Provide variety in your suggestions - different complexity levels, tech stacks, or approaches
User wants to create a new project with the following details: User wants to create a new project with the following details:
${input} ${input}
`, `,
}); });
@@ -182,7 +182,7 @@ export const suggestVariants = async ({
}), }),
prompt: ` prompt: `
Act as advanced DevOps engineer and generate docker compose with environment variables and domain configurations needed to install the following project. Act as advanced DevOps engineer and generate docker compose with environment variables and domain configurations needed to install the following project.
Return your response as a JSON object with this structure: Return your response as a JSON object with this structure:
{ {
"dockerCompose": "yaml string here", "dockerCompose": "yaml string here",
@@ -190,9 +190,9 @@ export const suggestVariants = async ({
"domains": [{"host": "domain.com", "port": 3000, "serviceName": "service"}], "domains": [{"host": "domain.com", "port": 3000, "serviceName": "service"}],
"configFiles": [{"content": "file content", "filePath": "path/to/file"}] "configFiles": [{"content": "file content", "filePath": "path/to/file"}]
} }
Note: configFiles is optional - only include it if configuration files are absolutely required. Note: configFiles is optional - only include it if configuration files are absolutely required.
Follow these rules: Follow these rules:
Docker Compose Rules: Docker Compose Rules:
@@ -242,16 +242,16 @@ export const suggestVariants = async ({
4. ONLY include environment variables that are actually used in the docker-compose 4. ONLY include environment variables that are actually used in the docker-compose
5. Every environment variable referenced in the docker-compose MUST have a corresponding entry in envVariables 5. Every environment variable referenced in the docker-compose MUST have a corresponding entry in envVariables
6. Do not include environment variables for services that don't exist in the docker-compose 6. Do not include environment variables for services that don't exist in the docker-compose
For each service that needs to be exposed to the internet: For each service that needs to be exposed to the internet:
1. Define a domain configuration with: 1. Define a domain configuration with:
- host: the domain name for the service in format: {service-name}-{random-3-chars-hex}-${ip ? ip.replaceAll(".", "-") : ""}.traefik.me - host: the domain name for the service in format: {service-name}-{random-3-chars-hex}-${ip ? ip.replaceAll(".", "-") : ""}.traefik.me
- port: the internal port the service runs on - port: the internal port the service runs on
- serviceName: the name of the service in the docker-compose - serviceName: the name of the service in the docker-compose
2. Make sure the service is properly configured to work with the specified port 2. Make sure the service is properly configured to work with the specified port
User's original request: ${input} User's original request: ${input}
Project details: Project details:
${suggestion?.description} ${suggestion?.description}
`, `,
+1 -1
View File
@@ -44,7 +44,7 @@ export const createDomain = async (input: typeof apiCreateDomain._type) => {
export const generateTraefikMeDomain = async ( export const generateTraefikMeDomain = async (
appName: string, appName: string,
userId: string, _userId: string,
serverId?: string, serverId?: string,
) => { ) => {
if (serverId) { if (serverId) {
@@ -235,7 +235,7 @@ const generateWildcardDomain = async (
baseDomain: string, baseDomain: string,
appName: string, appName: string,
serverIp: string, serverIp: string,
userId: string, _userId: string,
): Promise<string> => { ): Promise<string> => {
if (!baseDomain.startsWith("*.")) { if (!baseDomain.startsWith("*.")) {
throw new Error('The base domain must start with "*."'); throw new Error('The base domain must start with "*."');