feat: initial commit

This commit is contained in:
Mauricio Siu
2024-04-28 23:57:52 -06:00
parent 8857a20344
commit be56ba046c
412 changed files with 60777 additions and 1 deletions

View File

@@ -0,0 +1,59 @@
import React from "react";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { api } from "@/utils/api";
import { File } from "lucide-react";
import { UpdateTraefikConfig } from "./update-traefik-config";
interface Props {
applicationId: string;
}
export const ShowTraefikConfig = ({ applicationId }: Props) => {
const { data } = api.application.readTraefikConfig.useQuery(
{
applicationId,
},
{ enabled: !!applicationId },
);
return (
<Card className="bg-background">
<CardHeader className="flex flex-row justify-between">
<div>
<CardTitle className="text-xl">Traefik</CardTitle>
<CardDescription>
Modify the traefik config, in rare cases you may need to add
specific config, becarefull because modifying incorrectly can break
traefik and your application
</CardDescription>
</div>
</CardHeader>
<CardContent className="flex flex-col gap-4">
{data === null ? (
<div className="flex w-full flex-col items-center justify-center gap-3 pt-10">
<File className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground">
No traefik config detected
</span>
</div>
) : (
<div className="flex flex-col pt-2 relative">
<div className="flex flex-col gap-6 bg-input p-4 rounded-md max-h-[35rem] min-h-[10rem] overflow-y-auto">
<div>
<pre className="font-sans">{data || "Empty"}</pre>
</div>
<div className="flex justify-end absolute z-50 right-6">
<UpdateTraefikConfig applicationId={applicationId} />
</div>
</div>
</div>
)}
</CardContent>
</Card>
);
};

View File

@@ -0,0 +1,180 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Textarea } from "@/components/ui/textarea";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { AlertTriangle } from "lucide-react";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
import jsyaml from "js-yaml";
const UpdateTraefikConfigSchema = z.object({
traefikConfig: z.string(),
});
type UpdateTraefikConfig = z.infer<typeof UpdateTraefikConfigSchema>;
interface Props {
applicationId: string;
}
export const validateAndFormatYAML = (yamlText: string) => {
try {
const obj = jsyaml.load(yamlText);
const formattedYaml = jsyaml.dump(obj, { indent: 4 });
return { valid: true, formattedYaml, error: null };
} catch (error) {
if (error instanceof jsyaml.YAMLException) {
return {
valid: false,
formattedYaml: yamlText,
error: error.message,
};
}
return {
valid: false,
formattedYaml: yamlText,
error: "An unexpected error occurred while processing the YAML.",
};
}
};
export const UpdateTraefikConfig = ({ applicationId }: Props) => {
const { data, refetch } = api.application.readTraefikConfig.useQuery(
{
applicationId,
},
{ enabled: !!applicationId },
);
const { mutateAsync, isLoading, error, isError } =
api.application.updateTraefikConfig.useMutation();
const form = useForm<UpdateTraefikConfig>({
defaultValues: {
traefikConfig: "",
},
resolver: zodResolver(UpdateTraefikConfigSchema),
});
useEffect(() => {
if (data) {
form.reset({
traefikConfig: data || "",
});
}
}, [form, form.reset, data]);
const onSubmit = async (data: UpdateTraefikConfig) => {
const { valid, error } = validateAndFormatYAML(data.traefikConfig);
if (!valid) {
form.setError("traefikConfig", {
type: "manual",
message: error || "Invalid YAML",
});
return;
}
form.clearErrors("traefikConfig");
await mutateAsync({
applicationId,
traefikConfig: data.traefikConfig,
})
.then(async () => {
toast.success("Traefik config Updated");
refetch();
})
.catch(() => {
toast.error("Error to update the traefik config");
});
};
return (
<Dialog>
<DialogTrigger asChild>
<Button isLoading={isLoading}>Modify</Button>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-4xl">
<DialogHeader>
<DialogTitle>Update traefik config</DialogTitle>
<DialogDescription>Update the traefik config</DialogDescription>
</DialogHeader>
{isError && (
<div className="flex flex-row gap-4 rounded-lg bg-red-50 p-2 dark:bg-red-950">
<AlertTriangle className="text-red-600 dark:text-red-400" />
<span className="text-sm text-red-600 dark:text-red-400">
{error?.message}
</span>
</div>
)}
<Form {...form}>
<form
id="hook-form-update-traefik-config"
onSubmit={form.handleSubmit(onSubmit)}
className="grid w-full py-4"
>
<div className="flex flex-col">
<FormField
control={form.control}
name="traefikConfig"
render={({ field }) => (
<FormItem>
<FormLabel>Traefik config</FormLabel>
<FormControl>
<Textarea
className="h-[35rem]"
placeholder={`http:
routers:
router-name:
rule: Host('domain.com')
service: container-name
entryPoints:
- web
tls: false
middlewares: []
`}
{...field}
/>
</FormControl>
<pre>
<FormMessage />
</pre>
</FormItem>
)}
/>
</div>
</form>
<DialogFooter>
<Button
isLoading={isLoading}
form="hook-form-update-traefik-config"
type="submit"
>
Update
</Button>
</DialogFooter>
</Form>
</DialogContent>
</Dialog>
);
};