Merge branch 'canary' into 216-domains-for-services-created-via-template

This commit is contained in:
Mauricio Siu
2024-08-15 00:00:41 -06:00
67 changed files with 19595 additions and 11993 deletions
@@ -46,6 +46,7 @@ export const ShowTraefikConfig = ({ applicationId }: Props) => {
<div className="flex flex-col pt-2 relative">
<div className="flex flex-col gap-6 max-h-[35rem] min-h-[10rem] overflow-y-auto">
<CodeEditor
lineWrapping
value={data || "Empty"}
disabled
className="font-mono"
@@ -144,6 +144,7 @@ export const UpdateTraefikConfig = ({ applicationId }: Props) => {
<FormLabel>Traefik config</FormLabel>
<FormControl>
<CodeEditor
lineWrapping
wrapperClassName="h-[35rem] font-mono"
placeholder={`http:
routers:
@@ -3,6 +3,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
@@ -23,6 +24,7 @@ enum BuildType {
heroku_buildpacks = "heroku_buildpacks",
paketo_buildpacks = "paketo_buildpacks",
nixpacks = "nixpacks",
static = "static",
}
const mySchema = z.discriminatedUnion("buildType", [
@@ -34,6 +36,7 @@ const mySchema = z.discriminatedUnion("buildType", [
invalid_type_error: "Dockerfile path is required",
})
.min(1, "Dockerfile required"),
dockerContextPath: z.string().nullable().default(""),
}),
z.object({
buildType: z.literal("heroku_buildpacks"),
@@ -43,6 +46,10 @@ const mySchema = z.discriminatedUnion("buildType", [
}),
z.object({
buildType: z.literal("nixpacks"),
publishDirectory: z.string().optional(),
}),
z.object({
buildType: z.literal("static"),
}),
]);
@@ -73,17 +80,18 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
const buildType = form.watch("buildType");
useEffect(() => {
if (data) {
// TODO: refactor this
if (data.buildType === "dockerfile") {
form.reset({
buildType: data.buildType,
...(data.buildType && {
dockerfile: data.dockerfile || "",
dockerContextPath: data.dockerContextPath || "",
}),
});
} else {
form.reset({
buildType: data.buildType,
publishDirectory: data.publishDirectory || undefined,
});
}
}
@@ -93,7 +101,11 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
await mutateAsync({
applicationId,
buildType: data.buildType,
publishDirectory:
data.buildType === "nixpacks" ? data.publishDirectory : null,
dockerfile: data.buildType === "dockerfile" ? data.dockerfile : null,
dockerContextPath:
data.buildType === "dockerfile" ? data.dockerContextPath : null,
})
.then(async () => {
toast.success("Build type saved");
@@ -171,6 +183,12 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
Paketo Buildpacks
</FormLabel>
</FormItem>
<FormItem className="flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value="static" />
</FormControl>
<FormLabel className="font-normal">Static</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
<FormMessage />
@@ -179,16 +197,71 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => {
}}
/>
{buildType === "dockerfile" && (
<>
<FormField
control={form.control}
name="dockerfile"
render={({ field }) => {
return (
<FormItem>
<FormLabel>Docker File</FormLabel>
<FormControl>
<Input
placeholder={"Path of your docker file"}
{...field}
value={field.value ?? ""}
/>
</FormControl>
<FormMessage />
</FormItem>
);
}}
/>
<FormField
control={form.control}
name="dockerContextPath"
render={({ field }) => {
return (
<FormItem>
<FormLabel>Docker Context Path</FormLabel>
<FormControl>
<Input
placeholder={
"Path of your docker context default: ."
}
{...field}
value={field.value ?? ""}
/>
</FormControl>
<FormMessage />
</FormItem>
);
}}
/>
</>
)}
{buildType === "nixpacks" && (
<FormField
control={form.control}
name="dockerfile"
name="publishDirectory"
render={({ field }) => {
return (
<FormItem>
<FormLabel>Docker File</FormLabel>
<div className="space-y-0.5">
<FormLabel>Publish Directory</FormLabel>
<FormDescription>
Allows you to serve a single directory via NGINX after
the build phase. Useful if the final build assets
should be served as a static site.
</FormDescription>
</div>
<FormControl>
<Input
placeholder={"Path of your docker file"}
placeholder={"Publish Directory"}
{...field}
value={field.value ?? ""}
/>
@@ -53,7 +53,7 @@ export const ShowDeployments = ({ applicationId }: Props) => {
<div className="flex flex-row items-center gap-2 flex-wrap">
<span>Webhook URL: </span>
<div className="flex flex-row items-center gap-2">
<span className="text-muted-foreground">
<span className="break-all text-muted-foreground">
{`${url}/api/deploy/${data?.refreshToken}`}
</span>
<RefreshToken applicationId={applicationId} />
@@ -72,7 +72,7 @@ export const ShowDeployments = ({ applicationId }: Props) => {
{deployments?.map((deployment) => (
<div
key={deployment.deploymentId}
className="flex items-center justify-between rounded-lg border p-4"
className="flex items-center justify-between rounded-lg border p-4 gap-2"
>
<div className="flex flex-col">
<span className="flex items-center gap-4 font-medium capitalize text-foreground">
@@ -87,7 +87,7 @@ export const ShowDeployments = ({ applicationId }: Props) => {
{deployment.title}
</span>
{deployment.description && (
<span className="text-sm text-muted-foreground">
<span className="break-all text-sm text-muted-foreground">
{deployment.description}
</span>
)}
@@ -114,7 +114,7 @@ export const SaveDockerProvider = ({ applicationId }: Props) => {
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input placeholder="Password" {...field} />
<Input placeholder="Password" {...field} type="password" />
</FormControl>
<FormMessage />
</FormItem>
@@ -104,6 +104,7 @@ export const ShowTraefikFile = ({ path }: Props) => {
</FormDescription>
<FormControl>
<CodeEditor
lineWrapping
wrapperClassName="h-[35rem] font-mono"
placeholder={`http:
routers:
@@ -55,6 +55,7 @@ interface Props {
export const AddTemplate = ({ projectId }: Props) => {
const [query, setQuery] = useState("");
const [open, setOpen] = useState(false);
const { data } = api.compose.templates.useQuery();
const [selectedTags, setSelectedTags] = useState<string[]>([]);
const { data: tags, isLoading: isLoadingTags } =
@@ -75,14 +76,14 @@ export const AddTemplate = ({ projectId }: Props) => {
}) || [];
return (
<Dialog>
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger className="w-full">
<DropdownMenuItem
className="w-full cursor-pointer space-x-3"
onSelect={(e) => e.preventDefault()}
>
<PuzzleIcon className="size-4 text-muted-foreground" />
<span>Templates</span>
<span>Template</span>
</DropdownMenuItem>
</DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-7xl p-0">
@@ -283,6 +284,7 @@ export const AddTemplate = ({ projectId }: Props) => {
utils.project.one.invalidate({
projectId,
});
setOpen(false);
})
.catch(() => {
toast.error(
@@ -9,36 +9,11 @@ import {
import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { api } from "@/utils/api";
import { format } from "date-fns";
import { BadgeCheck } from "lucide-react";
import Link from "next/link";
import React, { useEffect, useState } from "react";
import { RemoveGithubApp } from "./remove-github-app";
export const generateName = () => {
const n1 = ["Blue", "Green", "Red", "Orange", "Violet", "Indigo", "Yellow"];
const n2 = [
"One",
"Two",
"Three",
"Four",
"Five",
"Six",
"Seven",
"Eight",
"Nine",
"Zero",
];
return `Dokploy-${n1[Math.round(Math.random() * (n1.length - 1))]}-${
n2[Math.round(Math.random() * (n2.length - 1))]
}`;
};
function slugify(text: string) {
return text
.toLowerCase()
.replace(/[\s\^&*()+=!]+/g, "-")
.replace(/[\$.,*+~()'"!:@^&]+/g, "")
.replace(/-+/g, "-")
.replace(/^-+|-+$/g, "");
}
export const GithubSetup = () => {
const [isOrganization, setIsOrganization] = useState(false);
@@ -52,10 +27,9 @@ export const GithubSetup = () => {
const manifest = JSON.stringify(
{
redirect_url: `${origin}/api/redirect?authId=${data?.authId}`,
name: generateName(),
name: `Dokploy-${format(new Date(), "yyyy-MM-dd")}`,
url: origin,
hook_attributes: {
// JUST FOR TESTING
url: `${url}/api/deploy/github`,
// url: `${origin}/api/webhook`, // Aquí especificas la URL del endpoint de tu webhook
},
@@ -95,8 +69,8 @@ export const GithubSetup = () => {
</div>
<div className="flex items-end gap-4 flex-wrap">
<RemoveGithubApp />
{/* <Link
href={`https://github.com/settings/apps/${data?.githubAppName}`}
<Link
href={`${data?.githubAppName}`}
target="_blank"
className={buttonVariants({
className: "w-fit",
@@ -104,7 +78,7 @@ export const GithubSetup = () => {
})}
>
<span className="text-sm">Manage Github App</span>
</Link> */}
</Link>
</div>
</div>
) : (
@@ -119,9 +93,9 @@ export const GithubSetup = () => {
<div className="flex flex-row gap-4">
<Link
href={`https://github.com/apps/${slugify(
data.githubAppName,
)}/installations/new?state=gh_setup:${data?.authId}`}
href={`${
data.githubAppName
}/installations/new?state=gh_setup:${data?.authId}`}
className={buttonVariants({ className: "w-fit" })}
>
Install Github App
@@ -22,7 +22,7 @@ export const ShowDestinations = () => {
<CardHeader>
<CardTitle className="text-xl">SSH Keys</CardTitle>
<CardDescription>
Use SSH to beeing able cloning from private repositories.
Use SSH to be able to clone from private repositories.
</CardDescription>
</CardHeader>
<CardContent className="space-y-2 pt-4">
@@ -39,6 +39,7 @@ const addPermissions = z.object({
canAccessToTraefikFiles: z.boolean().optional().default(false),
canAccessToDocker: z.boolean().optional().default(false),
canAccessToAPI: z.boolean().optional().default(false),
canAccessToSSHKeys: z.boolean().optional().default(false),
});
type AddPermissions = z.infer<typeof addPermissions>;
@@ -82,6 +83,7 @@ export const AddUserPermissions = ({ userId }: Props) => {
canAccessToTraefikFiles: data.canAccessToTraefikFiles,
canAccessToDocker: data.canAccessToDocker,
canAccessToAPI: data.canAccessToAPI,
canAccessToSSHKeys: data.canAccessToSSHKeys,
});
}
}, [form, form.formState.isSubmitSuccessful, form.reset, data]);
@@ -98,6 +100,7 @@ export const AddUserPermissions = ({ userId }: Props) => {
accesedServices: data.accesedServices || [],
canAccessToDocker: data.canAccessToDocker,
canAccessToAPI: data.canAccessToAPI,
canAccessToSSHKeys: data.canAccessToSSHKeys,
})
.then(async () => {
toast.success("Permissions updated");
@@ -270,6 +273,26 @@ export const AddUserPermissions = ({ userId }: Props) => {
</FormItem>
)}
/>
<FormField
control={form.control}
name="canAccessToSSHKeys"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<FormLabel>Access to SSH Keys</FormLabel>
<FormDescription>
Allow to users to access to the SSH Keys section
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="accesedProjects"
@@ -106,6 +106,7 @@ export const ShowMainTraefikConfig = ({ children }: Props) => {
<FormLabel>Traefik config</FormLabel>
<FormControl>
<CodeEditor
lineWrapping
wrapperClassName="h-[35rem] font-mono"
placeholder={`providers:
docker:
@@ -109,6 +109,7 @@ export const ShowServerTraefikConfig = ({ children }: Props) => {
<FormLabel>Traefik config</FormLabel>
<FormControl>
<CodeEditor
lineWrapping
wrapperClassName="h-[35rem] font-mono"
placeholder={`http:
routers:
@@ -79,6 +79,16 @@ export const SettingsLayout = ({ children }: Props) => {
},
]
: []),
...(user?.canAccessToSSHKeys
? [
{
title: "SSH Keys",
label: "",
icon: KeyRound,
href: "/dashboard/settings/ssh-keys",
},
]
: []),
]}
/>
</div>
@@ -3,6 +3,7 @@ import { json } from "@codemirror/lang-json";
import { yaml } from "@codemirror/lang-yaml";
import { StreamLanguage } from "@codemirror/language";
import { properties } from "@codemirror/legacy-modes/mode/properties";
import { EditorView } from "@codemirror/view";
import { githubDark, githubLight } from "@uiw/codemirror-theme-github";
import CodeMirror, { type ReactCodeMirrorProps } from "@uiw/react-codemirror";
import { useTheme } from "next-themes";
@@ -10,6 +11,7 @@ interface Props extends ReactCodeMirrorProps {
wrapperClassName?: string;
disabled?: boolean;
language?: "yaml" | "json" | "properties";
lineWrapping?: boolean;
}
export const CodeEditor = ({
@@ -36,6 +38,7 @@ export const CodeEditor = ({
: language === "json"
? json()
: StreamLanguage.define(properties),
props.lineWrapping ? EditorView.lineWrapping : [],
]}
{...props}
editable={!props.disabled}