mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Merge pull request #719 from Dokploy/443-implement-coolifyio-like-support-of-envs-and-railwayapp-envs-shared-project-service-env
feat: add shared enviroment variables
This commit is contained in:
162
apps/dokploy/components/dashboard/projects/add-env.tsx
Normal file
162
apps/dokploy/components/dashboard/projects/add-env.tsx
Normal file
@@ -0,0 +1,162 @@
|
||||
import { AlertBlock } from "@/components/shared/alert-block";
|
||||
import { CodeEditor } from "@/components/shared/code-editor";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { api } from "@/utils/api";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { AlertTriangle, FileIcon, SquarePen } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
|
||||
const updateProjectSchema = z.object({
|
||||
env: z.string().optional(),
|
||||
});
|
||||
|
||||
type UpdateProject = z.infer<typeof updateProjectSchema>;
|
||||
|
||||
interface Props {
|
||||
projectId: string;
|
||||
}
|
||||
|
||||
export const AddEnv = ({ projectId }: Props) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const utils = api.useUtils();
|
||||
const { mutateAsync, error, isError, isLoading } =
|
||||
api.project.update.useMutation();
|
||||
const { data } = api.project.one.useQuery(
|
||||
{
|
||||
projectId,
|
||||
},
|
||||
{
|
||||
enabled: !!projectId,
|
||||
},
|
||||
);
|
||||
|
||||
console.log(data);
|
||||
const form = useForm<UpdateProject>({
|
||||
defaultValues: {
|
||||
env: data?.env ?? "",
|
||||
},
|
||||
resolver: zodResolver(updateProjectSchema),
|
||||
});
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
form.reset({
|
||||
env: data.env ?? "",
|
||||
});
|
||||
}
|
||||
}, [data, form, form.reset]);
|
||||
|
||||
const onSubmit = async (formData: UpdateProject) => {
|
||||
await mutateAsync({
|
||||
env: formData.env || "",
|
||||
projectId: projectId,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success("Project env updated succesfully");
|
||||
utils.project.all.invalidate();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error to update the env");
|
||||
})
|
||||
.finally(() => {});
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<DropdownMenuItem
|
||||
className="w-full cursor-pointer space-x-3"
|
||||
onSelect={(e) => e.preventDefault()}
|
||||
>
|
||||
<FileIcon className="size-4" />
|
||||
<span>Add Env</span>
|
||||
</DropdownMenuItem>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-6xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Modify Shared Env</DialogTitle>
|
||||
<DialogDescription>Update the env variables</DialogDescription>
|
||||
</DialogHeader>
|
||||
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||
<AlertBlock type="info">
|
||||
To use a shared env, in one of your services, you need to use like
|
||||
this: Let's say you have a shared env ENVIROMENT="development" and you
|
||||
want to use it in your service, you need to use like this:
|
||||
<ul>
|
||||
<li>
|
||||
<code>ENVIRONMENT=${"${{shared.ENVIRONMENT}}"}</code>
|
||||
</li>
|
||||
<li>
|
||||
<code>DATABASE_URL=${"${{shared.DATABASE_URL}}"}</code>
|
||||
</li>
|
||||
</ul>{" "}
|
||||
This allows the service to inherit and use the shared variables from
|
||||
the project level, ensuring consistency across services.
|
||||
</AlertBlock>
|
||||
<div className="grid gap-4">
|
||||
<div className="grid items-center gap-4">
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="grid w-full gap-4 "
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="env"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Enviroment variables</FormLabel>
|
||||
<FormControl>
|
||||
<CodeEditor
|
||||
lineWrapping
|
||||
language="properties"
|
||||
wrapperClassName="h-[35rem] font-mono"
|
||||
placeholder={`NODE_ENV=production
|
||||
PORT=3000
|
||||
|
||||
`}
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<pre>
|
||||
<FormMessage />
|
||||
</pre>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<DialogFooter>
|
||||
<Button isLoading={isLoading} type="submit">
|
||||
Update
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
import Link from "next/link";
|
||||
import { Fragment } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { AddEnv } from "./add-env";
|
||||
import { UpdateProject } from "./update";
|
||||
|
||||
export const ShowProjects = () => {
|
||||
@@ -190,7 +191,9 @@ export const ShowProjects = () => {
|
||||
<DropdownMenuLabel className="font-normal">
|
||||
Actions
|
||||
</DropdownMenuLabel>
|
||||
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<AddEnv projectId={project.projectId} />
|
||||
</div>
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<UpdateProject projectId={project.projectId} />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user