refactor: improve error

This commit is contained in:
Mauricio Siu
2025-01-18 23:07:36 -06:00
parent 5e7d344110
commit e68465f9e6
2 changed files with 143 additions and 115 deletions

View File

@@ -16,6 +16,7 @@ import { useEffect, useState } from "react";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import { toast } from "sonner"; import { toast } from "sonner";
import type { TemplateInfo } from "./template-generator"; import type { TemplateInfo } from "./template-generator";
import { AlertBlock } from "@/components/shared/alert-block";
export interface StepProps { export interface StepProps {
nextStep: () => void; nextStep: () => void;
@@ -35,7 +36,8 @@ export const StepTwo = ({
useState<TemplateInfo["details"]>(); useState<TemplateInfo["details"]>();
const [showValues, setShowValues] = useState<Record<string, boolean>>({}); const [showValues, setShowValues] = useState<Record<string, boolean>>({});
const { mutateAsync, isLoading } = api.ai.suggest.useMutation(); const { mutateAsync, isLoading, error, isError } =
api.ai.suggest.useMutation();
useEffect(() => { useEffect(() => {
mutateAsync({ mutateAsync({
@@ -48,7 +50,6 @@ export const StepTwo = ({
setSuggestions(data); setSuggestions(data);
}) })
.catch((error) => { .catch((error) => {
console.error("Error details:", error);
toast.error("Error generating suggestions"); toast.error("Error generating suggestions");
}); });
}, [templateInfo.userInput]); }, [templateInfo.userInput]);
@@ -75,6 +76,7 @@ export const StepTwo = ({
if (!selectedVariant) return; if (!selectedVariant) return;
const updatedEnvVariables = [...selectedVariant.envVariables]; const updatedEnvVariables = [...selectedVariant.envVariables];
// @ts-ignore
updatedEnvVariables[index] = { updatedEnvVariables[index] = {
...updatedEnvVariables[index], ...updatedEnvVariables[index],
[field]: value, [field]: value,
@@ -151,7 +153,26 @@ export const StepTwo = ({
], ],
}); });
}; };
if (isError) {
return (
<div className="flex flex-col items-center justify-center h-full space-y-4">
<Bot className="w-16 h-16 text-primary animate-pulse" />
<h2 className="text-2xl font-semibold animate-pulse">Error</h2>
<AlertBlock type="error">
{error?.message || "Error generating suggestions"}
</AlertBlock>
<Button
onClick={() =>
selectedVariant ? setSelectedVariant(undefined) : prevStep()
}
variant="outline"
>
{selectedVariant ? "Change Variant" : "Back"}
</Button>
</div>
);
}
if (isLoading) { if (isLoading) {
return ( return (
<div className="flex flex-col items-center justify-center h-full space-y-4"> <div className="flex flex-col items-center justify-center h-full space-y-4">

View File

@@ -1,145 +1,152 @@
import { slugify } from "@/lib/slug"; import { slugify } from "@/lib/slug";
import { import {
adminProcedure, adminProcedure,
createTRPCRouter, createTRPCRouter,
protectedProcedure, protectedProcedure,
} from "@/server/api/trpc"; } from "@/server/api/trpc";
import { generatePassword } from "@/templates/utils"; import { generatePassword } from "@/templates/utils";
import { IS_CLOUD } from "@dokploy/server/constants"; import { IS_CLOUD } from "@dokploy/server/constants";
import { import {
apiCreateAi, apiCreateAi,
apiUpdateAi, apiUpdateAi,
deploySuggestionSchema, deploySuggestionSchema,
} from "@dokploy/server/db/schema/ai"; } from "@dokploy/server/db/schema/ai";
import { createDomain } from "@dokploy/server/index"; import { createDomain } from "@dokploy/server/index";
import { import {
deleteAiSettings, deleteAiSettings,
getAiSettingById, getAiSettingById,
getAiSettingsByAdminId, getAiSettingsByAdminId,
saveAiSettings, saveAiSettings,
suggestVariants, suggestVariants,
} from "@dokploy/server/services/ai"; } from "@dokploy/server/services/ai";
import { createComposeByTemplate } from "@dokploy/server/services/compose"; import { createComposeByTemplate } from "@dokploy/server/services/compose";
import { findProjectById } from "@dokploy/server/services/project"; import { findProjectById } from "@dokploy/server/services/project";
import { import {
addNewService, addNewService,
checkServiceAccess, checkServiceAccess,
} from "@dokploy/server/services/user"; } from "@dokploy/server/services/user";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { z } from "zod"; import { z } from "zod";
export const aiRouter = createTRPCRouter({ export const aiRouter = createTRPCRouter({
one: protectedProcedure one: protectedProcedure
.input(z.object({ aiId: z.string() })) .input(z.object({ aiId: z.string() }))
.query(async ({ ctx, input }) => { .query(async ({ ctx, input }) => {
const aiSetting = await getAiSettingById(input.aiId); const aiSetting = await getAiSettingById(input.aiId);
if (aiSetting.adminId !== ctx.user.adminId) { if (aiSetting.adminId !== ctx.user.adminId) {
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You don't have access to this AI configuration", message: "You don't have access to this AI configuration",
}); });
} }
return aiSetting; return aiSetting;
}), }),
create: adminProcedure.input(apiCreateAi).mutation(async ({ ctx, input }) => { create: adminProcedure.input(apiCreateAi).mutation(async ({ ctx, input }) => {
return await saveAiSettings(ctx.user.adminId, input); return await saveAiSettings(ctx.user.adminId, input);
}), }),
update: protectedProcedure update: protectedProcedure
.input(apiUpdateAi) .input(apiUpdateAi)
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
return await saveAiSettings(ctx.user.adminId, input); return await saveAiSettings(ctx.user.adminId, input);
}), }),
getAll: adminProcedure.query(async ({ ctx }) => { getAll: adminProcedure.query(async ({ ctx }) => {
return await getAiSettingsByAdminId(ctx.user.adminId); return await getAiSettingsByAdminId(ctx.user.adminId);
}), }),
get: protectedProcedure get: protectedProcedure
.input(z.object({ aiId: z.string() })) .input(z.object({ aiId: z.string() }))
.query(async ({ ctx, input }) => { .query(async ({ ctx, input }) => {
const aiSetting = await getAiSettingById(input.aiId); const aiSetting = await getAiSettingById(input.aiId);
if (aiSetting.adminId !== ctx.user.authId) { if (aiSetting.adminId !== ctx.user.authId) {
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You don't have access to this AI configuration", message: "You don't have access to this AI configuration",
}); });
} }
return aiSetting; return aiSetting;
}), }),
delete: protectedProcedure delete: protectedProcedure
.input(z.object({ aiId: z.string() })) .input(z.object({ aiId: z.string() }))
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const aiSetting = await getAiSettingById(input.aiId); const aiSetting = await getAiSettingById(input.aiId);
if (aiSetting.adminId !== ctx.user.adminId) { if (aiSetting.adminId !== ctx.user.adminId) {
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You don't have access to this AI configuration", message: "You don't have access to this AI configuration",
}); });
} }
return await deleteAiSettings(input.aiId); return await deleteAiSettings(input.aiId);
}), }),
suggest: protectedProcedure suggest: protectedProcedure
.input( .input(
z.object({ z.object({
aiId: z.string(), aiId: z.string(),
input: z.string(), input: z.string(),
serverId: z.string().optional(), serverId: z.string().optional(),
}), })
) )
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
return await suggestVariants({ try {
...input, return await suggestVariants({
adminId: ctx.user.adminId, ...input,
}); adminId: ctx.user.adminId,
}), });
deploy: protectedProcedure } catch (error) {
.input(deploySuggestionSchema) throw new TRPCError({
.mutation(async ({ ctx, input }) => { code: "BAD_REQUEST",
if (ctx.user.rol === "user") { message: error instanceof Error ? error?.message : `Error: ${error}`,
await checkServiceAccess(ctx.user.adminId, input.projectId, "create"); });
} }
}),
deploy: protectedProcedure
.input(deploySuggestionSchema)
.mutation(async ({ ctx, input }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.adminId, input.projectId, "create");
}
if (IS_CLOUD && !input.serverId) { if (IS_CLOUD && !input.serverId) {
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You need to use a server to create a compose", message: "You need to use a server to create a compose",
}); });
} }
const project = await findProjectById(input.projectId); const project = await findProjectById(input.projectId);
const projectName = slugify(`${project.name} ${input.id}`); const projectName = slugify(`${project.name} ${input.id}`);
console.log(input); console.log(input);
const compose = await createComposeByTemplate({ const compose = await createComposeByTemplate({
...input, ...input,
composeFile: input.dockerCompose, composeFile: input.dockerCompose,
env: input.envVariables, env: input.envVariables,
serverId: input.serverId, serverId: input.serverId,
name: input.name, name: input.name,
sourceType: "raw", sourceType: "raw",
appName: `${projectName}-${generatePassword(6)}`, appName: `${projectName}-${generatePassword(6)}`,
}); });
if (input.domains && input.domains?.length > 0) { if (input.domains && input.domains?.length > 0) {
for (const domain of input.domains) { for (const domain of input.domains) {
await createDomain({ await createDomain({
...domain, ...domain,
domainType: "compose", domainType: "compose",
certificateType: "none", certificateType: "none",
composeId: compose.composeId, composeId: compose.composeId,
}); });
} }
} }
if (ctx.user.rol === "user") { if (ctx.user.rol === "user") {
await addNewService(ctx.user.authId, compose.composeId); await addNewService(ctx.user.authId, compose.composeId);
} }
return null; return null;
}), }),
}); });