mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat: add domains
This commit is contained in:
parent
08ab18eebf
commit
87546b4558
@ -1,90 +0,0 @@
|
|||||||
import { CodeEditor } from "@/components/shared/code-editor";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
||||||
import ReactMarkdown from "react-markdown";
|
|
||||||
import type { StepProps } from "./step-two";
|
|
||||||
|
|
||||||
export const StepFour = ({
|
|
||||||
prevStep,
|
|
||||||
templateInfo,
|
|
||||||
setOpen,
|
|
||||||
setTemplateInfo,
|
|
||||||
}: StepProps) => {
|
|
||||||
const handleSubmit = () => {
|
|
||||||
setTemplateInfo(templateInfo); // Update the template info
|
|
||||||
setOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col h-full">
|
|
||||||
<div className="flex-grow">
|
|
||||||
<div className="space-y-6">
|
|
||||||
<h2 className="text-lg font-semibold">Step 4: Review and Finalize</h2>
|
|
||||||
<ScrollArea className="h-[400px] p-5">
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div>
|
|
||||||
<h3 className="text-sm font-semibold">Name</h3>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{templateInfo?.details?.name}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="text-sm font-semibold">Description</h3>
|
|
||||||
<ReactMarkdown className="text-sm text-muted-foreground">
|
|
||||||
{templateInfo?.details?.description}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="text-md font-semibold">Server</h3>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{templateInfo?.serverId || "Dokploy Server"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="text-sm font-semibold">Docker Compose</h3>
|
|
||||||
<CodeEditor
|
|
||||||
lineWrapping
|
|
||||||
value={templateInfo?.details?.dockerCompose}
|
|
||||||
disabled
|
|
||||||
className="font-mono"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="text-sm font-semibold">Environment Variables</h3>
|
|
||||||
<ul className="list-disc pl-5">
|
|
||||||
{templateInfo?.details?.envVariables.map(
|
|
||||||
(
|
|
||||||
env: {
|
|
||||||
name: string;
|
|
||||||
value: string;
|
|
||||||
},
|
|
||||||
index: number,
|
|
||||||
) => (
|
|
||||||
<li key={index}>
|
|
||||||
<strong className="text-sm font-semibold">
|
|
||||||
{env.name}
|
|
||||||
</strong>
|
|
||||||
:
|
|
||||||
<span className="text-sm ml-2 text-muted-foreground">
|
|
||||||
{env.value}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ScrollArea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="sticky bottom-0 bg-background pt-2 border-t">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<Button onClick={prevStep} variant="outline">
|
|
||||||
Back
|
|
||||||
</Button>
|
|
||||||
<Button onClick={handleSubmit}>Create</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -3,7 +3,17 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectLabel,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { api } from "@/utils/api";
|
||||||
|
|
||||||
const examples = [
|
const examples = [
|
||||||
"Make a personal blog",
|
"Make a personal blog",
|
||||||
@ -16,8 +26,14 @@ const examples = [
|
|||||||
export const StepOne = ({ nextStep, setTemplateInfo, templateInfo }: any) => {
|
export const StepOne = ({ nextStep, setTemplateInfo, templateInfo }: any) => {
|
||||||
const [userInput, setUserInput] = useState(templateInfo.userInput);
|
const [userInput, setUserInput] = useState(templateInfo.userInput);
|
||||||
|
|
||||||
|
// Get servers from the API
|
||||||
|
const { data: servers } = api.server.withSSHKey.useQuery();
|
||||||
|
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
setTemplateInfo({ ...templateInfo, userInput });
|
setTemplateInfo({
|
||||||
|
...templateInfo,
|
||||||
|
userInput,
|
||||||
|
});
|
||||||
nextStep();
|
nextStep();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,9 +42,9 @@ export const StepOne = ({ nextStep, setTemplateInfo, templateInfo }: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full gap-4">
|
||||||
<div className="flex-grow overflow-auto">
|
<div className="">
|
||||||
<div className="space-y-4 pb-20">
|
<div className="space-y-4 ">
|
||||||
<h2 className="text-lg font-semibold">Step 1: Describe Your Needs</h2>
|
<h2 className="text-lg font-semibold">Step 1: Describe Your Needs</h2>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="user-needs">Describe your template needs</Label>
|
<Label htmlFor="user-needs">Describe your template needs</Label>
|
||||||
@ -40,6 +56,39 @@ export const StepOne = ({ nextStep, setTemplateInfo, templateInfo }: any) => {
|
|||||||
className="min-h-[100px]"
|
className="min-h-[100px]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Examples:</Label>
|
<Label>Examples:</Label>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
@ -57,7 +106,7 @@ export const StepOne = ({ nextStep, setTemplateInfo, templateInfo }: any) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="sticky bottom-0 bg-background pt-2 border-t">
|
<div className="">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<Button onClick={handleNext} disabled={!userInput.trim()}>
|
<Button onClick={handleNext} disabled={!userInput.trim()}>
|
||||||
Next
|
Next
|
||||||
|
@ -1,93 +1,104 @@
|
|||||||
"use client";
|
import { CodeEditor } from "@/components/shared/code-editor";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import ReactMarkdown from "react-markdown";
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectGroup,
|
|
||||||
SelectItem,
|
|
||||||
SelectLabel,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/components/ui/select";
|
|
||||||
import { api } from "@/utils/api";
|
|
||||||
import { useState } from "react";
|
|
||||||
import type { StepProps } from "./step-two";
|
import type { StepProps } from "./step-two";
|
||||||
|
|
||||||
export const StepThree = ({
|
export const StepThree = ({
|
||||||
nextStep,
|
|
||||||
prevStep,
|
prevStep,
|
||||||
templateInfo,
|
templateInfo,
|
||||||
setTemplateInfo,
|
onSubmit,
|
||||||
}: StepProps) => {
|
}: StepProps & { onSubmit: () => void }) => {
|
||||||
const [server, setServer] = useState(templateInfo.serverId);
|
|
||||||
const { data: servers } = api.server.withSSHKey.useQuery();
|
|
||||||
|
|
||||||
const handleNext = () => {
|
|
||||||
const updatedInfo = { ...templateInfo, name };
|
|
||||||
if (server?.trim()) {
|
|
||||||
updatedInfo.serverId = server;
|
|
||||||
}
|
|
||||||
setTemplateInfo(updatedInfo);
|
|
||||||
nextStep();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full">
|
||||||
<div className="flex-grow overflow-auto">
|
<div className="flex-grow">
|
||||||
<div className="space-y-4 pb-20">
|
<div className="space-y-6">
|
||||||
<h2 className="text-lg font-semibold">Step 3: Additional Details</h2>
|
<h2 className="text-lg font-semibold">Step 3: Review and Finalize</h2>
|
||||||
<div>
|
<div className="space-y-4">
|
||||||
<Label htmlFor="name">App Name</Label>
|
<div>
|
||||||
<Input
|
<h3 className="text-sm font-semibold">Name</h3>
|
||||||
id="name"
|
<p className="text-sm text-muted-foreground">
|
||||||
value={templateInfo?.details?.name || ""}
|
{templateInfo?.details?.name}
|
||||||
onChange={(e) => {
|
</p>
|
||||||
setTemplateInfo({
|
</div>
|
||||||
...templateInfo,
|
<div>
|
||||||
details: {
|
<h3 className="text-sm font-semibold">Description</h3>
|
||||||
...templateInfo?.details,
|
<ReactMarkdown className="text-sm text-muted-foreground">
|
||||||
name: e.target.value,
|
{templateInfo?.details?.description}
|
||||||
},
|
</ReactMarkdown>
|
||||||
});
|
</div>
|
||||||
}}
|
<div>
|
||||||
placeholder="Enter app name"
|
<h3 className="text-md font-semibold">Server</h3>
|
||||||
className="mt-1"
|
<p className="text-sm text-muted-foreground">
|
||||||
/>
|
{templateInfo?.server?.name || "Dokploy Server"}
|
||||||
</div>
|
</p>
|
||||||
<div>
|
</div>
|
||||||
<Label htmlFor="server">Server Attachment (Optional)</Label>
|
<div className="space-y-2">
|
||||||
<Select value={server} onValueChange={setServer}>
|
<h3 className="text-sm font-semibold">Docker Compose</h3>
|
||||||
<SelectTrigger>
|
<CodeEditor
|
||||||
<SelectValue placeholder="Select a Server" />
|
lineWrapping
|
||||||
</SelectTrigger>
|
value={templateInfo?.details?.dockerCompose}
|
||||||
<SelectContent>
|
disabled
|
||||||
<SelectGroup>
|
className="font-mono"
|
||||||
{servers?.map((server) => (
|
/>
|
||||||
<SelectItem key={server.serverId} value={server.serverId}>
|
</div>
|
||||||
{server.name}
|
<div>
|
||||||
</SelectItem>
|
<h3 className="text-sm font-semibold">Environment Variables</h3>
|
||||||
))}
|
<ul className="list-disc pl-5">
|
||||||
<SelectLabel>Servers ({servers?.length})</SelectLabel>
|
{templateInfo?.details?.envVariables.map(
|
||||||
</SelectGroup>
|
(
|
||||||
</SelectContent>
|
env: {
|
||||||
</Select>
|
name: string;
|
||||||
|
value: string;
|
||||||
|
},
|
||||||
|
index: number,
|
||||||
|
) => (
|
||||||
|
<li key={index}>
|
||||||
|
<strong className="text-sm font-semibold">
|
||||||
|
{env.name}
|
||||||
|
</strong>
|
||||||
|
:
|
||||||
|
<span className="text-sm ml-2 text-muted-foreground">
|
||||||
|
{env.value}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-sm font-semibold">Domains</h3>
|
||||||
|
<ul className="list-disc pl-5">
|
||||||
|
{templateInfo?.details?.domains.map(
|
||||||
|
(
|
||||||
|
domain: {
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
serviceName: string;
|
||||||
|
},
|
||||||
|
index: number,
|
||||||
|
) => (
|
||||||
|
<li key={index}>
|
||||||
|
<strong className="text-sm font-semibold">
|
||||||
|
{domain.host}
|
||||||
|
</strong>
|
||||||
|
:
|
||||||
|
<span className="text-sm ml-2 text-muted-foreground">
|
||||||
|
{domain.port} - {domain.serviceName}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="sticky bottom-0 bg-background pt-2 border-t">
|
<div className="pt-6">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<Button onClick={prevStep} variant="outline">
|
<Button onClick={prevStep} variant="outline">
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button onClick={onSubmit}>Create</Button>
|
||||||
onClick={handleNext}
|
|
||||||
disabled={!templateInfo?.details?.name?.trim()}
|
|
||||||
>
|
|
||||||
Next
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,7 +40,8 @@ export const StepTwo = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
mutateAsync({
|
mutateAsync({
|
||||||
aiId: templateInfo.aiId,
|
aiId: templateInfo.aiId,
|
||||||
prompt: templateInfo.userInput,
|
serverId: templateInfo.server?.serverId || "",
|
||||||
|
input: templateInfo.userInput,
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
@ -62,59 +63,98 @@ export const StepTwo = ({
|
|||||||
nextStep();
|
nextStep();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEnvVariableChange = (
|
|
||||||
index: number,
|
|
||||||
field: string,
|
|
||||||
value: string,
|
|
||||||
) => {
|
|
||||||
// const updatedEnvVariables = [...envVariables];
|
|
||||||
// if (updatedEnvVariables[index]) {
|
|
||||||
// updatedEnvVariables[index] = {
|
|
||||||
// ...updatedEnvVariables[index],
|
|
||||||
// [field]: value,
|
|
||||||
// };
|
|
||||||
// setEnvVariables(updatedEnvVariables);
|
|
||||||
// }
|
|
||||||
};
|
|
||||||
|
|
||||||
// const addEnvVariable = () => {
|
|
||||||
// setEnvVariables([...envVariables, { name: "", value: "" }]);
|
|
||||||
// setShowValues((prev) => ({ ...prev, "": false }));
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const removeEnvVariable = (index: number) => {
|
|
||||||
// const updatedEnvVariables = envVariables.filter((_, i) => i !== index);
|
|
||||||
// setEnvVariables(updatedEnvVariables);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const handleDomainChange = (
|
|
||||||
// index: number,
|
|
||||||
// field: string,
|
|
||||||
// value: string | number,
|
|
||||||
// ) => {
|
|
||||||
// const updatedDomains = [...domains];
|
|
||||||
// if (updatedDomains[index]) {
|
|
||||||
// updatedDomains[index] = {
|
|
||||||
// ...updatedDomains[index],
|
|
||||||
// [field]: value,
|
|
||||||
// };
|
|
||||||
// setDomains(updatedDomains);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const addDomain = () => {
|
|
||||||
// setDomains([...domains, { host: "", port: 0, serviceName: "" }]);
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const removeDomain = (index: number) => {
|
|
||||||
// const updatedDomains = domains.filter((_, i) => i !== index);
|
|
||||||
// setDomains(updatedDomains);
|
|
||||||
// };
|
|
||||||
|
|
||||||
const toggleShowValue = (name: string) => {
|
const toggleShowValue = (name: string) => {
|
||||||
setShowValues((prev) => ({ ...prev, [name]: !prev[name] }));
|
setShowValues((prev) => ({ ...prev, [name]: !prev[name] }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleEnvVariableChange = (
|
||||||
|
index: number,
|
||||||
|
field: "name" | "value",
|
||||||
|
value: string,
|
||||||
|
) => {
|
||||||
|
if (!selectedVariant) return;
|
||||||
|
|
||||||
|
const updatedEnvVariables = [...selectedVariant.envVariables];
|
||||||
|
updatedEnvVariables[index] = {
|
||||||
|
...updatedEnvVariables[index],
|
||||||
|
[field]: value,
|
||||||
|
};
|
||||||
|
|
||||||
|
setSelectedVariant({
|
||||||
|
...selectedVariant,
|
||||||
|
envVariables: updatedEnvVariables,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeEnvVariable = (index: number) => {
|
||||||
|
if (!selectedVariant) return;
|
||||||
|
|
||||||
|
const updatedEnvVariables = selectedVariant.envVariables.filter(
|
||||||
|
(_, i) => i !== index,
|
||||||
|
);
|
||||||
|
|
||||||
|
setSelectedVariant({
|
||||||
|
...selectedVariant,
|
||||||
|
envVariables: updatedEnvVariables,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDomainChange = (
|
||||||
|
index: number,
|
||||||
|
field: "host" | "port" | "serviceName",
|
||||||
|
value: string | number,
|
||||||
|
) => {
|
||||||
|
if (!selectedVariant) return;
|
||||||
|
|
||||||
|
const updatedDomains = [...selectedVariant.domains];
|
||||||
|
updatedDomains[index] = {
|
||||||
|
...updatedDomains[index],
|
||||||
|
[field]: value,
|
||||||
|
};
|
||||||
|
|
||||||
|
setSelectedVariant({
|
||||||
|
...selectedVariant,
|
||||||
|
domains: updatedDomains,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeDomain = (index: number) => {
|
||||||
|
if (!selectedVariant) return;
|
||||||
|
|
||||||
|
const updatedDomains = selectedVariant.domains.filter(
|
||||||
|
(_, i) => i !== index,
|
||||||
|
);
|
||||||
|
|
||||||
|
setSelectedVariant({
|
||||||
|
...selectedVariant,
|
||||||
|
domains: updatedDomains,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const addEnvVariable = () => {
|
||||||
|
if (!selectedVariant) return;
|
||||||
|
|
||||||
|
setSelectedVariant({
|
||||||
|
...selectedVariant,
|
||||||
|
envVariables: [
|
||||||
|
...selectedVariant.envVariables,
|
||||||
|
{ name: "", value: "" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const addDomain = () => {
|
||||||
|
if (!selectedVariant) return;
|
||||||
|
|
||||||
|
setSelectedVariant({
|
||||||
|
...selectedVariant,
|
||||||
|
domains: [
|
||||||
|
...selectedVariant.domains,
|
||||||
|
{ host: "", port: 80, serviceName: "" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
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">
|
||||||
@ -131,7 +171,7 @@ export const StepTwo = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full gap-6">
|
||||||
<div className="flex-grow overflow-auto pb-8">
|
<div className="flex-grow overflow-auto pb-8">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<h2 className="text-lg font-semibold">Step 2: Choose a Variant</h2>
|
<h2 className="text-lg font-semibold">Step 2: Choose a Variant</h2>
|
||||||
@ -207,7 +247,7 @@ export const StepTwo = ({
|
|||||||
<AccordionItem value="env-variables">
|
<AccordionItem value="env-variables">
|
||||||
<AccordionTrigger>Environment Variables</AccordionTrigger>
|
<AccordionTrigger>Environment Variables</AccordionTrigger>
|
||||||
<AccordionContent>
|
<AccordionContent>
|
||||||
<ScrollArea className="h-[300px] w-full rounded-md border">
|
<ScrollArea className=" w-full rounded-md border">
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-4 space-y-4">
|
||||||
{selectedVariant?.envVariables.map((env, index) => (
|
{selectedVariant?.envVariables.map((env, index) => (
|
||||||
<div
|
<div
|
||||||
@ -270,7 +310,7 @@ export const StepTwo = ({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
// onClick={addEnvVariable}
|
onClick={addEnvVariable}
|
||||||
>
|
>
|
||||||
<PlusCircle className="h-4 w-4 mr-2" />
|
<PlusCircle className="h-4 w-4 mr-2" />
|
||||||
Add Variable
|
Add Variable
|
||||||
@ -308,7 +348,7 @@ export const StepTwo = ({
|
|||||||
handleDomainChange(
|
handleDomainChange(
|
||||||
index,
|
index,
|
||||||
"port",
|
"port",
|
||||||
parseInt(e.target.value),
|
Number.parseInt(e.target.value),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
placeholder="Port"
|
placeholder="Port"
|
||||||
@ -341,7 +381,7 @@ export const StepTwo = ({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
// onClick={addDomain}
|
onClick={addDomain}
|
||||||
>
|
>
|
||||||
<PlusCircle className="h-4 w-4 mr-2" />
|
<PlusCircle className="h-4 w-4 mr-2" />
|
||||||
Add Domain
|
Add Domain
|
||||||
@ -356,7 +396,7 @@ export const StepTwo = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="sticky bottom-0 bg-background pt-5 border-t">
|
<div className="">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<Button
|
<Button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@ -17,14 +17,12 @@ import {
|
|||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { Bot } from "lucide-react";
|
import { Bot } from "lucide-react";
|
||||||
|
import Link from "next/link";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { StepFour } from "./step-four";
|
|
||||||
import { StepOne } from "./step-one";
|
import { StepOne } from "./step-one";
|
||||||
import { StepThree } from "./step-three";
|
import { StepThree } from "./step-three";
|
||||||
import { StepTwo } from "./step-two";
|
import { StepTwo } from "./step-two";
|
||||||
import { AlertBlock } from "@/components/shared/alert-block";
|
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
interface EnvVariable {
|
interface EnvVariable {
|
||||||
name: string;
|
name: string;
|
||||||
@ -37,9 +35,7 @@ interface Domain {
|
|||||||
serviceName: string;
|
serviceName: string;
|
||||||
}
|
}
|
||||||
export interface TemplateInfo {
|
export interface TemplateInfo {
|
||||||
id: string;
|
|
||||||
userInput: string;
|
userInput: string;
|
||||||
type: string;
|
|
||||||
details?: {
|
details?: {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
@ -49,15 +45,17 @@ export interface TemplateInfo {
|
|||||||
shortDescription: string;
|
shortDescription: string;
|
||||||
domains: Domain[];
|
domains: Domain[];
|
||||||
};
|
};
|
||||||
serverId?: string;
|
server?: {
|
||||||
|
serverId: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
aiId: string;
|
aiId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultTemplateInfo: TemplateInfo = {
|
const defaultTemplateInfo: TemplateInfo = {
|
||||||
id: "",
|
|
||||||
aiId: "",
|
aiId: "",
|
||||||
userInput: "",
|
userInput: "",
|
||||||
type: "",
|
server: undefined,
|
||||||
details: {
|
details: {
|
||||||
id: "",
|
id: "",
|
||||||
name: "",
|
name: "",
|
||||||
@ -83,7 +81,7 @@ export const TemplateGenerator = ({ projectId }: Props) => {
|
|||||||
useState<TemplateInfo>(defaultTemplateInfo);
|
useState<TemplateInfo>(defaultTemplateInfo);
|
||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
|
|
||||||
const totalSteps = 4;
|
const totalSteps = 3;
|
||||||
|
|
||||||
const nextStep = () => setStep((prev) => Math.min(prev + 1, totalSteps));
|
const nextStep = () => setStep((prev) => Math.min(prev + 1, totalSteps));
|
||||||
const prevStep = () => setStep((prev) => Math.max(prev - 1, 1));
|
const prevStep = () => setStep((prev) => Math.max(prev - 1, 1));
|
||||||
@ -118,7 +116,7 @@ export const TemplateGenerator = ({ projectId }: Props) => {
|
|||||||
Create a custom template based on your needs
|
Create a custom template based on your needs
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="mt-4 flex-grow overflow-auto">
|
<div className="mt-4 ">
|
||||||
{step === 1 && (
|
{step === 1 && (
|
||||||
<>
|
<>
|
||||||
{!haveAtleasOneProviderEnabled && (
|
{!haveAtleasOneProviderEnabled && (
|
||||||
@ -192,29 +190,25 @@ export const TemplateGenerator = ({ projectId }: Props) => {
|
|||||||
)}
|
)}
|
||||||
{step === 3 && (
|
{step === 3 && (
|
||||||
<StepThree
|
<StepThree
|
||||||
|
prevStep={prevStep}
|
||||||
|
templateInfo={templateInfo}
|
||||||
nextStep={nextStep}
|
nextStep={nextStep}
|
||||||
prevStep={prevStep}
|
|
||||||
templateInfo={templateInfo}
|
|
||||||
setTemplateInfo={setTemplateInfo}
|
setTemplateInfo={setTemplateInfo}
|
||||||
/>
|
onSubmit={async () => {
|
||||||
)}
|
console.log("Submitting template:", templateInfo);
|
||||||
{step === 4 && (
|
|
||||||
<StepFour
|
|
||||||
prevStep={prevStep}
|
|
||||||
templateInfo={templateInfo}
|
|
||||||
setTemplateInfo={async (data: any) => {
|
|
||||||
console.log("Submitting template:", data);
|
|
||||||
setTemplateInfo(data);
|
|
||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
projectId,
|
projectId,
|
||||||
id: templateInfo.details?.id || "",
|
id: templateInfo.details?.id || "",
|
||||||
name: templateInfo?.details?.name || "",
|
name: templateInfo?.details?.name || "",
|
||||||
description: data?.details?.shortDescription || "",
|
description: templateInfo?.details?.shortDescription || "",
|
||||||
dockerCompose: data?.details?.dockerCompose || "",
|
dockerCompose: templateInfo?.details?.dockerCompose || "",
|
||||||
envVariables: (data?.details?.envVariables || [])
|
envVariables: (templateInfo?.details?.envVariables || [])
|
||||||
.map((env: any) => `${env.name}=${env.value}`)
|
.map((env: any) => `${env.name}=${env.value}`)
|
||||||
.join("\n"),
|
.join("\n"),
|
||||||
serverId: data.server || "",
|
domains: templateInfo?.details?.domains || [],
|
||||||
|
...(templateInfo.server?.serverId && {
|
||||||
|
serverId: templateInfo.server?.serverId || "",
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
toast.success("Compose Created");
|
toast.success("Compose Created");
|
||||||
@ -227,7 +221,6 @@ export const TemplateGenerator = ({ projectId }: Props) => {
|
|||||||
toast.error("Error creating the compose");
|
toast.error("Error creating the compose");
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
setOpen={setOpen}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { DialogAction } from "@/components/shared/dialog-action";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@ -12,7 +13,6 @@ import { api } from "@/utils/api";
|
|||||||
import { BotIcon, Loader2, Trash2 } from "lucide-react";
|
import { BotIcon, Loader2, Trash2 } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { HandleAi } from "./handle-ai";
|
import { HandleAi } from "./handle-ai";
|
||||||
import { DialogAction } from "@/components/shared/dialog-action";
|
|
||||||
|
|
||||||
export const AiForm = () => {
|
export const AiForm = () => {
|
||||||
const { data: aiConfigs, refetch, isLoading } = api.ai.getAll.useQuery();
|
const { data: aiConfigs, refetch, isLoading } = api.ai.getAll.useQuery();
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
import { AlertBlock } from "@/components/shared/alert-block";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@ -18,22 +27,13 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { PenBoxIcon, PlusIcon } from "lucide-react";
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { z } from "zod";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "@/components/ui/dialog";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { PenBoxIcon, PlusIcon } from "lucide-react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { AlertBlock } from "@/components/shared/alert-block";
|
import { z } from "zod";
|
||||||
|
|
||||||
const Schema = z.object({
|
const Schema = z.object({
|
||||||
name: z.string().min(1, { message: "Name is required" }),
|
name: z.string().min(1, { message: "Name is required" }),
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
Bell,
|
Bell,
|
||||||
BlocksIcon,
|
BlocksIcon,
|
||||||
BookIcon,
|
BookIcon,
|
||||||
|
BotIcon,
|
||||||
Boxes,
|
Boxes,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
CircleHelp,
|
CircleHelp,
|
||||||
@ -19,7 +20,6 @@ import {
|
|||||||
GitBranch,
|
GitBranch,
|
||||||
HeartIcon,
|
HeartIcon,
|
||||||
KeyRound,
|
KeyRound,
|
||||||
BotIcon,
|
|
||||||
type LucideIcon,
|
type LucideIcon,
|
||||||
Package,
|
Package,
|
||||||
PieChart,
|
PieChart,
|
||||||
|
@ -11,11 +11,12 @@ import {
|
|||||||
apiUpdateAi,
|
apiUpdateAi,
|
||||||
deploySuggestionSchema,
|
deploySuggestionSchema,
|
||||||
} from "@dokploy/server/db/schema/ai";
|
} from "@dokploy/server/db/schema/ai";
|
||||||
|
import { createDomain } from "@dokploy/server/index";
|
||||||
import {
|
import {
|
||||||
getAiSettingsByAdminId,
|
|
||||||
getAiSettingById,
|
|
||||||
saveAiSettings,
|
|
||||||
deleteAiSettings,
|
deleteAiSettings,
|
||||||
|
getAiSettingById,
|
||||||
|
getAiSettingsByAdminId,
|
||||||
|
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";
|
||||||
@ -84,11 +85,15 @@ export const aiRouter = createTRPCRouter({
|
|||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
aiId: z.string(),
|
aiId: z.string(),
|
||||||
prompt: z.string(),
|
input: z.string(),
|
||||||
|
serverId: z.string().optional(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
return await suggestVariants(ctx.user.adminId, input.aiId, input.prompt);
|
return await suggestVariants({
|
||||||
|
...input,
|
||||||
|
adminId: ctx.user.adminId,
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
deploy: protectedProcedure
|
deploy: protectedProcedure
|
||||||
.input(deploySuggestionSchema)
|
.input(deploySuggestionSchema)
|
||||||
@ -108,6 +113,8 @@ export const aiRouter = createTRPCRouter({
|
|||||||
|
|
||||||
const projectName = slugify(`${project.name} ${input.id}`);
|
const projectName = slugify(`${project.name} ${input.id}`);
|
||||||
|
|
||||||
|
console.log(input);
|
||||||
|
|
||||||
const compose = await createComposeByTemplate({
|
const compose = await createComposeByTemplate({
|
||||||
...input,
|
...input,
|
||||||
composeFile: input.dockerCompose,
|
composeFile: input.dockerCompose,
|
||||||
@ -118,6 +125,17 @@ export const aiRouter = createTRPCRouter({
|
|||||||
appName: `${projectName}-${generatePassword(6)}`,
|
appName: `${projectName}-${generatePassword(6)}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (input.domains && input.domains?.length > 0) {
|
||||||
|
for (const domain of input.domains) {
|
||||||
|
await createDomain({
|
||||||
|
...domain,
|
||||||
|
domainType: "compose",
|
||||||
|
certificateType: "none",
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
@ -3,119 +3,119 @@ import { boolean, integer, pgTable, text } from "drizzle-orm/pg-core";
|
|||||||
import { createInsertSchema } from "drizzle-zod";
|
import { createInsertSchema } from "drizzle-zod";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
import { ai } from "./ai";
|
||||||
import { auth } from "./auth";
|
import { auth } from "./auth";
|
||||||
import { certificates } from "./certificate";
|
import { certificates } from "./certificate";
|
||||||
import { registry } from "./registry";
|
import { registry } from "./registry";
|
||||||
import { certificateType } from "./shared";
|
import { certificateType } from "./shared";
|
||||||
import { sshKeys } from "./ssh-key";
|
import { sshKeys } from "./ssh-key";
|
||||||
import { users } from "./user";
|
import { users } from "./user";
|
||||||
import { ai } from "./ai";
|
|
||||||
|
|
||||||
export const admins = pgTable("admin", {
|
export const admins = pgTable("admin", {
|
||||||
adminId: text("adminId")
|
adminId: text("adminId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.primaryKey()
|
.primaryKey()
|
||||||
.$defaultFn(() => nanoid()),
|
.$defaultFn(() => nanoid()),
|
||||||
serverIp: text("serverIp"),
|
serverIp: text("serverIp"),
|
||||||
certificateType: certificateType("certificateType").notNull().default("none"),
|
certificateType: certificateType("certificateType").notNull().default("none"),
|
||||||
host: text("host"),
|
host: text("host"),
|
||||||
letsEncryptEmail: text("letsEncryptEmail"),
|
letsEncryptEmail: text("letsEncryptEmail"),
|
||||||
sshPrivateKey: text("sshPrivateKey"),
|
sshPrivateKey: text("sshPrivateKey"),
|
||||||
enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false),
|
enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false),
|
||||||
enableLogRotation: boolean("enableLogRotation").notNull().default(false),
|
enableLogRotation: boolean("enableLogRotation").notNull().default(false),
|
||||||
authId: text("authId")
|
authId: text("authId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => auth.id, { onDelete: "cascade" }),
|
.references(() => auth.id, { onDelete: "cascade" }),
|
||||||
createdAt: text("createdAt")
|
createdAt: text("createdAt")
|
||||||
.notNull()
|
.notNull()
|
||||||
.$defaultFn(() => new Date().toISOString()),
|
.$defaultFn(() => new Date().toISOString()),
|
||||||
stripeCustomerId: text("stripeCustomerId"),
|
stripeCustomerId: text("stripeCustomerId"),
|
||||||
stripeSubscriptionId: text("stripeSubscriptionId"),
|
stripeSubscriptionId: text("stripeSubscriptionId"),
|
||||||
serversQuantity: integer("serversQuantity").notNull().default(0),
|
serversQuantity: integer("serversQuantity").notNull().default(0),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const adminsRelations = relations(admins, ({ one, many }) => ({
|
export const adminsRelations = relations(admins, ({ one, many }) => ({
|
||||||
auth: one(auth, {
|
auth: one(auth, {
|
||||||
fields: [admins.authId],
|
fields: [admins.authId],
|
||||||
references: [auth.id],
|
references: [auth.id],
|
||||||
}),
|
}),
|
||||||
users: many(users),
|
users: many(users),
|
||||||
registry: many(registry),
|
registry: many(registry),
|
||||||
sshKeys: many(sshKeys),
|
sshKeys: many(sshKeys),
|
||||||
certificates: many(certificates),
|
certificates: many(certificates),
|
||||||
ai: many(ai),
|
ai: many(ai),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const createSchema = createInsertSchema(admins, {
|
const createSchema = createInsertSchema(admins, {
|
||||||
adminId: z.string(),
|
adminId: z.string(),
|
||||||
enableDockerCleanup: z.boolean().optional(),
|
enableDockerCleanup: z.boolean().optional(),
|
||||||
sshPrivateKey: z.string().optional(),
|
sshPrivateKey: z.string().optional(),
|
||||||
certificateType: z.enum(["letsencrypt", "none"]).default("none"),
|
certificateType: z.enum(["letsencrypt", "none"]).default("none"),
|
||||||
serverIp: z.string().optional(),
|
serverIp: z.string().optional(),
|
||||||
letsEncryptEmail: z.string().optional(),
|
letsEncryptEmail: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiUpdateAdmin = createSchema.partial();
|
export const apiUpdateAdmin = createSchema.partial();
|
||||||
|
|
||||||
export const apiSaveSSHKey = createSchema
|
export const apiSaveSSHKey = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
sshPrivateKey: true,
|
sshPrivateKey: true,
|
||||||
})
|
})
|
||||||
.required();
|
.required();
|
||||||
|
|
||||||
export const apiAssignDomain = createSchema
|
export const apiAssignDomain = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
host: true,
|
host: true,
|
||||||
certificateType: true,
|
certificateType: true,
|
||||||
letsEncryptEmail: true,
|
letsEncryptEmail: true,
|
||||||
})
|
})
|
||||||
.required()
|
.required()
|
||||||
.partial({
|
.partial({
|
||||||
letsEncryptEmail: true,
|
letsEncryptEmail: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiUpdateDockerCleanup = createSchema
|
export const apiUpdateDockerCleanup = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
enableDockerCleanup: true,
|
enableDockerCleanup: true,
|
||||||
})
|
})
|
||||||
.required()
|
.required()
|
||||||
.extend({
|
.extend({
|
||||||
serverId: z.string().optional(),
|
serverId: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiTraefikConfig = z.object({
|
export const apiTraefikConfig = z.object({
|
||||||
traefikConfig: z.string().min(1),
|
traefikConfig: z.string().min(1),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiModifyTraefikConfig = z.object({
|
export const apiModifyTraefikConfig = z.object({
|
||||||
path: z.string().min(1),
|
path: z.string().min(1),
|
||||||
traefikConfig: z.string().min(1),
|
traefikConfig: z.string().min(1),
|
||||||
serverId: z.string().optional(),
|
serverId: z.string().optional(),
|
||||||
});
|
});
|
||||||
export const apiReadTraefikConfig = z.object({
|
export const apiReadTraefikConfig = z.object({
|
||||||
path: z.string().min(1),
|
path: z.string().min(1),
|
||||||
serverId: z.string().optional(),
|
serverId: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiEnableDashboard = z.object({
|
export const apiEnableDashboard = z.object({
|
||||||
enableDashboard: z.boolean().optional(),
|
enableDashboard: z.boolean().optional(),
|
||||||
serverId: z.string().optional(),
|
serverId: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiServerSchema = z
|
export const apiServerSchema = z
|
||||||
.object({
|
.object({
|
||||||
serverId: z.string().optional(),
|
serverId: z.string().optional(),
|
||||||
})
|
})
|
||||||
.optional();
|
.optional();
|
||||||
|
|
||||||
export const apiReadStatsLogs = z.object({
|
export const apiReadStatsLogs = z.object({
|
||||||
page: z
|
page: z
|
||||||
.object({
|
.object({
|
||||||
pageIndex: z.number(),
|
pageIndex: z.number(),
|
||||||
pageSize: z.number(),
|
pageSize: z.number(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
status: z.string().array().optional(),
|
status: z.string().array().optional(),
|
||||||
search: z.string().optional(),
|
search: z.string().optional(),
|
||||||
sort: z.object({ id: z.string(), desc: z.boolean() }).optional(),
|
sort: z.object({ id: z.string(), desc: z.boolean() }).optional(),
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
import { relations } from "drizzle-orm";
|
||||||
import { boolean, pgTable, text } from "drizzle-orm/pg-core";
|
import { boolean, pgTable, text } from "drizzle-orm/pg-core";
|
||||||
import { createInsertSchema } from "drizzle-zod";
|
import { createInsertSchema } from "drizzle-zod";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { admins } from "./admin";
|
import { admins } from "./admin";
|
||||||
import { relations } from "drizzle-orm";
|
|
||||||
import { nanoid } from "nanoid";
|
|
||||||
|
|
||||||
export const ai = pgTable("ai", {
|
export const ai = pgTable("ai", {
|
||||||
aiId: text("aiId")
|
aiId: text("aiId")
|
||||||
@ -63,4 +63,13 @@ export const deploySuggestionSchema = z.object({
|
|||||||
serverId: z.string().optional(),
|
serverId: z.string().optional(),
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
|
domains: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
host: z.string().min(1),
|
||||||
|
port: z.number().min(1),
|
||||||
|
serviceName: z.string().min(1),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
});
|
});
|
||||||
|
@ -7,6 +7,7 @@ import { desc, eq } from "drizzle-orm";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { IS_CLOUD } from "../constants";
|
import { IS_CLOUD } from "../constants";
|
||||||
import { findAdminById } from "./admin";
|
import { findAdminById } from "./admin";
|
||||||
|
import { findServerById } from "./server";
|
||||||
|
|
||||||
export const getAiSettingsByAdminId = async (adminId: string) => {
|
export const getAiSettingsByAdminId = async (adminId: string) => {
|
||||||
const aiSettings = await db.query.ai.findMany({
|
const aiSettings = await db.query.ai.findMany({
|
||||||
@ -51,11 +52,19 @@ export const deleteAiSettings = async (aiId: string) => {
|
|||||||
return db.delete(ai).where(eq(ai.aiId, aiId));
|
return db.delete(ai).where(eq(ai.aiId, aiId));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const suggestVariants = async (
|
interface Props {
|
||||||
adminId: string,
|
adminId: string;
|
||||||
aiId: string,
|
aiId: string;
|
||||||
input: string
|
input: string;
|
||||||
) => {
|
serverId?: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const suggestVariants = async ({
|
||||||
|
adminId,
|
||||||
|
aiId,
|
||||||
|
input,
|
||||||
|
serverId,
|
||||||
|
}: Props) => {
|
||||||
try {
|
try {
|
||||||
const aiSettings = await getAiSettingById(aiId);
|
const aiSettings = await getAiSettingById(aiId);
|
||||||
if (!aiSettings || !aiSettings.isEnabled) {
|
if (!aiSettings || !aiSettings.isEnabled) {
|
||||||
@ -74,6 +83,13 @@ export const suggestVariants = async (
|
|||||||
ip = admin?.serverIp || "";
|
ip = admin?.serverIp || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (serverId) {
|
||||||
|
const server = await findServerById(serverId);
|
||||||
|
ip = server.ipAddress;
|
||||||
|
} else if (process.env.NODE_ENV === "development") {
|
||||||
|
ip = "127.0.0.1";
|
||||||
|
}
|
||||||
|
|
||||||
const { object } = await generateObject({
|
const { object } = await generateObject({
|
||||||
model,
|
model,
|
||||||
output: "array",
|
output: "array",
|
||||||
@ -122,13 +138,13 @@ export const suggestVariants = async (
|
|||||||
2. Use complex values for passwords/secrets variables
|
2. Use complex values for passwords/secrets variables
|
||||||
3. Don't set container_name field in services
|
3. Don't set container_name field in services
|
||||||
4. Don't set version field in the docker compose
|
4. Don't set version field in the docker compose
|
||||||
5. Don't set ports like 'ports: 3000:3000', use 'ports: ["3000"]' instead
|
5. Don't set ports like 'ports: 3000:3000', use 'ports: "3000"' instead
|
||||||
6. Use dokploy-network in all services
|
6. Use dokploy-network in all services
|
||||||
7. Add dokploy-network at the end and mark it as external: true
|
7. Add dokploy-network at the end and mark it as external: true
|
||||||
|
|
||||||
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
|
- 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 in the docker-compose to work with the specified port
|
2. Make sure the service is properly configured in the docker-compose to work with the specified port
|
||||||
@ -145,7 +161,6 @@ export const suggestVariants = async (
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error in docker compose generation:", error);
|
console.error("Error in docker compose generation:", error);
|
||||||
console.error("Error details:", error.cause?.issues || error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
Loading…
Reference in New Issue
Block a user